Как превратить прямое конвейерное выражение в выражение вычисления?

Итак, я хочу создать собственное выражение вычисления, которое позволило бы мне повернуть это -

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

Возможно ли это или что-то близкое к этому? Если да, то какой подход можно применить? Это монада или что-то меньшее?


person Bryan Edds    schedule 15.10.2013    source источник
comment
Просто интересно, какая польза от использования вычислительных выражений? (мне нравится конвейерная нотация ...)   -  person Tomas Petricek    schedule 15.10.2013
comment
Проще говоря, возможность устанавливать точки останова для каждой отдельной операции. Трубопроводный практически не может быть обнаружен, AFAICT. По правде говоря, я бы предпочел, чтобы VS позволял ставить точку останова внутри конвейера, но я не вижу этого. Помимо всего этого, я, возможно, хочу добавить несколько пользовательских операторов CE. В конечном итоге я пытаюсь создать небольшое «скриптовое» выражение для вычислений на уровне типа World.   -  person Bryan Edds    schedule 15.10.2013
comment
Конечно, мой комментарий предполагает, что есть какой-то способ вывести текущий мир из отладчика ... Но, может быть, этого и не будет :( ... Может, мне просто нужно вернуться к нормальному коду, основанному на let?   -  person Bryan Edds    schedule 15.10.2013
comment
Похоже, что вам нужна монада состояния, где мир - это состояние   -  person Ankur    schedule 15.10.2013
comment
Кажется, что tech.blinemedical.com/debugging-piped-sequences-f быть в некоторой степени полезным для того, чего вы пытаетесь достичь   -  person N_A    schedule 15.10.2013
comment
@Arkur - Я попробовал монаду State, и она работает, но она была намного более подробной, чем то, что я хотел, чтобы код был. В этот момент я подумал, что могу вернуться к серии выражений let. : /   -  person Bryan Edds    schedule 15.10.2013
comment
@mydogisbox, я ценю это предложение, но для меня это все еще недостаточно удобно для отладки. Даже выборочного переопределения оператора | ›и его взлома просто ... нет.   -  person Bryan Edds    schedule 15.10.2013


Ответы (4)


Может, мне просто стоит вернуться к этому -

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 }

Он отлично отлаживает (вы можете найти все предыдущие версии tw_ в окне Autos и можете выполнять пошаговую отладку для каждой операции), и я полагаю, что это не слишком многословно.

person Bryan Edds    schedule 15.10.2013
comment
как я мог этого не видеть. это именно то, что я имел в виду. Я думаю, монада здесь не послужит тебе лучше - person nicolas; 15.10.2013

Если вы используете библиотеку ExtCore (доступную в NuGet), она предоставляет оператор с именем _ 1_, который специально предназначен для помощи в отладке конвейерных выражений. Вы используете его, «касаясь» своего конвейера, который применяет функцию действия (возвращает 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. для вставки отладочных утверждений в ваш конвейер.

person Jack P.    schedule 15.10.2013
comment
Я ценю это предложение, но мне действительно нужно иметь возможность вставлять точки останова без перезапуска программы. - person Bryan Edds; 15.10.2013

Состояние или монада писателя могут быть полезны, но если вы хотите использовать отладчик, забудьте о монадах, это еще хуже. Единственное, что вы можете сделать, - это переопределить оператор конвейера следующим образом:

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 в порядке, хотя вам нужно изменить свой код, и вы потеряете канал вперед. В этом случае может быть лучше добавить точку останова в тело функции (|>).

person Gus    schedule 15.10.2013
comment
Хех, это круто, но я определенно не хочу печатать мир :) В защиту монад, по крайней мере, вы можете поставить точку останова на каждой операции - только не пытайтесь пошагово отлаживать :) - person Bryan Edds; 15.10.2013
comment
Вы также можете установить точку останова в теле переопределения этого оператора. Просто сделайте это многострочным, я добавил комментарий в последнем примере. - person Gus; 15.10.2013

Вам понадобится let! для распространения монадического состояния.

Итак, я думаю, у вас уже есть монада: это простой старый let, который берет текущие привязки, добавляет новый и передает его в последующих вычислениях!

person nicolas    schedule 15.10.2013