4

我正在努力定义一个有状态的构建器,但我无法解决一些编译器错误

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

谢谢

4

1 回答 1

3

您似乎在这里有一个单子类型,它不能“包含”任何东西(即 Async<'a> 可以包含'a 的类型)。

这意味着能够绑定的唯一合理类型是unit,这使得 bind 的签名member __.Bind(m: MovementState, f : unit -> MovementState)

这允许您使用do!符号来操作您的移动状态列表,这意味着您需要重写左右方法。我相信您也需要在构建器上使用 combine 方法,但是编译器会很快通知您是否这样做!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)
    }
于 2017-06-14T09:58:24.830 回答