我正在尝试编写一个计算工作流程,它允许计算产生副作用,如日志或睡眠以及返回值
一个用例就是这样的
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)
有什么建议吗?
答案 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))