如何制作懒惰的计算工作流程?

时间:2016-11-21 17:20:56

标签: f#

我正在尝试编写一个计算工作流程,它允许计算产生副作用,如日志或睡眠以及返回值

一个用例就是这样的

let add x y =
    compute {
        do! log (sprintf "add: x = %d, y= %d" x y) 
        do! sleep 1000
        let r = x + y
        do! log (sprintf "add: result= %d" r)
        return r
    }
...
let result = run (add 100 1000)

我希望在调用executeComputation时产生副作用。

我的尝试是

type Effect =    
    | Log of string
    | Sleep of int

type Computation<'t> = Computation of Lazy<'t * Effect list>

let private bind (u : 'u, effs : Effect list) 
         (f : 'u -> 'v * Effect list) 
         : ('v * Effect list) =
    let v, newEffs = f u
    let allEffects = List.append effs newEffs
    v, allEffects

type ComputeBuilder() =
    member this.Zero() = lazy ((), [])

    member this.Return(x) = x, []

    member this.ReturnFrom(Computation f) = f.Force()

    member this.Bind(x, f) = bind x f

    member this.Delay(funcToDelay) = funcToDelay

    member this.Run(funcToRun) = Computation (lazy funcToRun())

let compute = new ComputeBuilder()

let log msg = (), [Log msg]
let sleep ms = (), [Sleep ms]

let run (Computation x) = x.Force()

...但是编译器抱怨了!以下代码中的行:

let x = 
    compute {
        let! a = add 10 20
        let! b = add 11 2000
        return a + b
        }

Error FS0001: This expression was expected to have type
    'a * Effect list
but here has type
    Computation<'b> (FS0001)

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

对你的定义不正确的主要问题是计算构建器的某些成员使用你的Computation<'T>类型,而其他一些成员直接使用一对值和效果列表。

要进行类型检查,您需要保持一致。以下版本在任何地方使用Computation<'T> - 查看Bind的类型签名,例如:

let private bind (Computation c) f : Computation<_> = 
    Computation(Lazy.Create(fun () ->
      let u, effs = c.Value
      let (Computation(c2)) = f u
      let v, newEffs = c2.Value
      let allEffects = List.append effs newEffs
      v, allEffects))

type ComputeBuilder() =
    member this.Zero() = Computation(lazy ((), []))
    member this.Return(x) = Computation(lazy (x, []))
    member this.ReturnFrom(c) = c
    member this.Bind(x, f) = bind x f
    member this.Delay(funcToDelay:_ -> Computation<_>) = 
      Computation(Lazy.Create(fun () -> 
        let (Computation(r)) = funcToDelay()
        r.Value))