1

所以,我想构建一个自定义计算表达式,让我可以把它变成 -

testWorld |>
    subscribe ClickTestButtonAddress [] addBoxes |>
    addScreen testScreen TestScreenAddress |>
    setP (Some TestScreenAddress) World.optActiveScreenAddress |>
    addGroup testGroup TestGroupAddress |>
    addEntityGuiLabel (testLabelGuiEntity, testLabelGui, testLabel) TestLabelAddress |>
    addEntityGuiButton (testButtonGuiEntity, testButtonGui, testButton) TestButtonAddress |>
    addEntityActorBlock (testFloorActorEntity, testFloorActor, testFloor) TestFloorAddress |>
    (let hintRenderingPackageUse = HintRenderingPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HRPU = () }
     fun world -> { world with RenderMessages = hintRenderingPackageUse :: world.RenderMessages }) |>
    (let hintAudioPackageUse = HintAudioPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HAPU = () }
     fun world -> { world with AudioMessages = hintAudioPackageUse :: world.AudioMessages })

变成这样的东西 -

fwd {
    do! subscribe ClickTestButtonAddress [] addBoxes
    do! addScreen testScreen TestScreenAddress
    do! setP (Some TestScreenAddress) World.optActiveScreenAddress
    do! addGroup testGroup TestGroupAddress
    do! addEntityGuiLabel (testLabelGuiEntity, testLabelGui, testLabel) TestLabelAddress
    do! addEntityGuiButton (testButtonGuiEntity, testButtonGui, testButton) TestButtonAddress
    do! addEntityActorBlock (testFloorActorEntity, testFloorActor, testFloor) TestFloorAddress
    let hintRenderingPackageUse = HintRenderingPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HRPU = () }
    do! fun world -> { world with RenderMessages = hintRenderingPackageUse :: world.RenderMessages }
    let hintAudioPackageUse = HintAudioPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HAPU = () }
    do! fun world -> { world with AudioMessages = hintAudioPackageUse :: world.AudioMessages }}
    <| runFwd testWorld

这是可能的,还是接近可能的?如果是这样,可以采取什么方法?这是一个单子,还是更小的东西?

4

4 回答 4

6

也许我应该回去做这件事——

let tw_ = testWorld
let tw_ = subscribe ClickTestButtonAddress [] addBoxes tw_
let tw_ = addScreen testScreen TestScreenAddress tw_
let tw_ = setP (Some TestScreenAddress) World.optActiveScreenAddress tw_
let tw_ = addGroup testGroup TestGroupAddress tw_
let tw_ = addEntityGuiLabel (testLabelGuiEntity, testLabelGui, testLabel) TestLabelAddress tw_
let tw_ = addEntityGuiButton (testButtonGuiEntity, testButtonGui, testButton) TestButtonAddress tw_
let tw_ = addEntityActorBlock (testFloorActorEntity, testFloorActor, testFloor) TestFloorAddress tw_
let tw_ = { tw_ with RenderMessages = hintRenderingPackageUse :: tw_.RenderMessages }
{ tw_ with AudioMessages = hintAudioPackageUse :: tw_.AudioMessages }

它调试得很好(你可以在 Autos 窗口中找到所有以前版本的 tw_ 并且可以对每个操作进行单步调试),而且我想它并不太冗长。

于 2013-10-15T07:50:53.113 回答
2

如果您使用ExtCore库(可在 NuGet 上获得),它会提供一个名为的运算符,该运算符tap专门用于帮助调试流水线表达式。您可以通过“轻敲”管道来使用它,该管道将操作函数(返回unit)应用于管道中某个点的值,然后传递该值,使其按预期继续流经管道。

例如:

testWorld
|> subscribe ClickTestButtonAddress [] addBoxes
|> addScreen testScreen TestScreenAddress
// Check to see if the screen was added correctly
|> tap (fun world ->
    // TODO : Insert code to check if the screen was added.
    // Or, put some dummy code here so you can set a breakpoint
    // on it to inspect 'world' in the debugger.
    ())
|> setP (Some TestScreenAddress) World.optActiveScreenAddress
|> addGroup testGroup TestGroupAddress
|> addEntityGuiLabel (testLabelGuiEntity, testLabelGui, testLabel) TestLabelAddress
|> addEntityGuiButton (testButtonGuiEntity, testButtonGui, testButton) TestButtonAddress
|> addEntityActorBlock (testFloorActorEntity, testFloorActor, testFloor) TestFloorAddress
|> (let hintRenderingPackageUse = HintRenderingPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HRPU = () }
    fun world -> { world with RenderMessages = hintRenderingPackageUse :: world.RenderMessages }) 
|> (let hintAudioPackageUse = HintAudioPackageUse { FileName = "AssetGraph.xml"; PackageName = "Misc"; HAPU = () }
    fun world -> { world with AudioMessages = hintAudioPackageUse :: world.AudioMessages })

相关函数tapAssert可用于将调试断言插入到您的管道中。

于 2013-10-15T12:51:19.517 回答
2

State 或 Writer Monad 可能很有用,但如果你想使用调试器忘记 Monad,那就更糟了。您可以做的一件事是重新定义管道运算符,如下所示:

let (|>) a b = printfn "Value is: %A" a; b a

5 
    |> ((+) 40) 
    |> string 
    |> Seq.singleton  
    |> Seq.toArray

你会看到的:

Value is: 5
Value is: 45
Value is: "45"
Value is: seq ["45"]
val it : string [] = [|"45"|]

或者,您可以不打印,而是将结果累积到一个可变的对象列表中。

let mutable results = [] : obj list
let (|>) a b = 
    results <- results @ [box a] // or set a breakpoint here
    b a  
...
val mutable results : obj list = [5; 45; "45"; <seq>]

这几乎就像作家单子。

但是,如果您确实想使用调试器(它不是我最喜欢的工具),那么您的解决方案let很好,尽管您必须更改代码并且向前松开管道,在这种情况下,最好在函数体(|>)

于 2013-10-15T14:39:39.507 回答
1

你需要 alet!来传播一元状态。

所以我猜你已经有了monad:它是普通的旧的let,它采用当前的绑定,添加一个新的,并将它传递给后续的计算!

于 2013-10-15T08:41:17.077 回答