Как я могу реализовать Stateful Builder с помощью CustomOperations, а некоторые пусть

Я пытаюсь разобраться с определением конструктора с отслеживанием состояния и не могу обойти некоторые ошибки компилятора.

type Movement =
    | Left of int
    | Right of int

type MovementState = Movement list -> Movement list

type MovementBuilder () =
    member x.Zero () : MovementState = id
    member __.Return x : MovementState = id
    member __.Bind(m: MovementState, f: MovementState ) = fun v -> f (m v)

    [<CustomOperation("left", MaintainsVariableSpaceUsingBind = true)>]
    member x.Left(ms, value) = x.Bind(ms, fun xs -> xs @ [Left value])

    [<CustomOperation("right", MaintainsVariableSpaceUsingBind = true)>]
    member x.Right(ms, value) = x.Bind(ms, fun xs -> xs @ [Right value])

let movement = MovementBuilder()

[]
|> movement {
    left 10
    right 20
}
|> printfn "list %A"
//prints [Left 10; Right 20]

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

[]
|> movement {
    left 10
    let! _ = (fun xs -> xs @ [Right 99])
    //I also tried naming the value
    //let! x = (fun xs -> xs @ [Right 99])
    //I also tried wrapping it into another function ...
    //let! x = fun () -> (fun xs -> xs @ [Right 99])
    right 20
}
|> printfn "list %A"
//Should print [Left 10; Right 99; Right 20]

Любая помощь приветствуется. Bonus Karma будет отправлена ​​за объяснение того, как компилятор переписывает это в серию Binds

Спасибо


person robkuz    schedule 14.06.2017    source источник
comment
почему бы вам не добавить обычный параметр, подобный этому: gist.github.com/CarstenKoenig/32701ed30577fc7128abd150c579cc01 ? - если вас не волнует эта часть монады, вы можете просто использовать вместо нее Movement list (на самом деле вы не делаете больше) - просто комментирую, потому что я действительно не знаю, что вы пытаетесь сделать   -  person Random Dev    schedule 14.06.2017
comment
Спасибо за ваш пример - можете ли вы заставить его работать с yield вместо do!?   -  person robkuz    schedule 14.06.2017
comment
да, вы можете (я обновил суть) - но в этом случае я бы не включал left/right в конструктор, а просто сделал бы их функциями   -  person Random Dev    schedule 15.06.2017


Ответы (1)


Кажется, здесь у вас есть монадический тип, который не может ничего «содержать» (т. е. Async‹'a> может содержать типы 'a).

Это означает, что единственным разумным типом, который можно связать, является unit, что делает сигнатуру связывания member __.Bind(m: MovementState, f : unit -> MovementState).

Это позволяет вам использовать нотацию do! для манипулирования вашим списком состояний перемещения и будет означать немного переписывание ваших левых и правых методов. Я считаю, что вам также понадобится метод комбинирования в вашем компоновщике, но компилятор сообщит вам, сделаете вы это или нет, довольно быстро! Нотация let! здесь не имеет особого смысла, так как у вас нет «содержащегося» типа для развертывания.

У меня есть короткий пример этого в сообщении в блоге, наиболее подходящий код ниже:

type PTD = ProvidedTypeDefinition -> ProvidedTypeDefinition

type ProvidedTypeBuilder () =
    member __.Zero () : PTD =
        id
    member __.Return _ : PTD =
        id
    member __.Bind(m, f : unit -> PTD) =
        fun ptd -> (f ()) (m ptd)
    member x.Combine(m1 : PTD, m2 : PTD) : PTD =
        x.Bind(m1, fun () -> m2)

    [<CustomOperation("addMember", MaintainsVariableSpaceUsingBind = true)>]
    member x.AddMember(ptd, member') =
        let func =
          fun (instance : ProvidedTypeDefinition) ->
              instance.AddMember member'
              instance
        x.Bind(ptd, fun () -> func)

В качестве примера того, как вы можете использовать нотацию do!, вы можете сделать что-то вроде этого, а не создавать собственную операцию:

let ptd = ProvidedTypeBuilder()

let test =
    ptd {
        addMember (ProvidedProperty("MyProp", typeof<string>))
        do! (fun ptd -> ptd.AddObsoleteAttribute("Hey, don't use this anymore"); ptd)
    }
person mavnn    schedule 14.06.2017
comment
Я знаю ваш пост в блоге (спасибо за это). Однако в вашем примере используются только пользовательские операции, а не комбинация операций с yield или do!, и это именно то, что я ищу. - person robkuz; 14.06.2017
comment
@robkuz Я добавил пример использования do! обозначение с CE I определено выше. - person mavnn; 14.06.2017
comment
@robkuz просто член Yield и, возможно, YieldFrom (Combine уже здесь) — я обновил свою суть, и вы можете найти подробности здесь: docs.microsoft.com/en-us/dotnet/fsharp/language-reference/ - person Random Dev; 15.06.2017