我希望构建一个计算表达式,其中我可以表达以下内容:
let x = someComputationExpression {
do! "Message 1"
printfn "something 1"
do! "Message 2"
printfn "something 2"
do! "Message 3"
printfn "something 3"
let lastValue = 4
do! "Message 4"
// need to reference values across `do!`
printfn "something %s" lastValue
}
并且能够从CCD_ 1获取列表:
[| "Message 1"
"Message 2"
"Message 3"
"Message 4" |]
不需要调用printfn
,但可以稍后执行它(如果有意义的话)。
它不需要使用do!
关键字,它可以是yield
或return
,无论它工作需要什么。
换句话说,我希望能够在计算表达式中收集一些状态,并将稍后可以执行的工作(printfn
)排队。
我试过几件事,但不确定是否可能。
从OP问题中找出精确的解决方案有点困难。相反,我将发布一些OP可能可以根据需要进行调整的代码。
我定义了Result和ResultGenerator
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
生成器生成一个值以及直接值和延迟值的列表,直接值是上面的字符串列表,但与它们混合的是延迟值。我喜欢混合退货,这样订单就可以保留下来。
请注意,这是一个有时被称为State
monad的版本。
除了类CE组件(如bind
和Builders)之外,我还创建了两个直接和延迟的函数。
direct
用于创建直接值,x
0用于创建延迟值(取函数)
let direct v : ResultGenerator<_> =
G <| fun rs ->
(), Direct v::rs
let delayed d : ResultGenerator<_> =
G <| fun rs ->
(), Delayed d::rs
为了提高可读性,我定义了延迟trace
函数:
let trace m : ResultGenerator<_> =
G <| fun rs ->
(), Delayed (fun () -> printfn "%s" m)::rs
let tracef fmt = kprintf trace fmt
来自示例生成器:
let test =
builder {
do! direct "Hello"
do! tracef "A trace:%s" "!"
do! direct "There"
return 123
}
实现了以下结果:
(123, [Direct "Hello"; Delayed <fun:trace@37-1>; Direct "There"])
(延迟将在执行时打印跟踪)。
希望这能给如何解决实际问题提供一些思路。
完整来源:
open FStharp.Core.Printf
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
let value v : ResultGenerator<_> =
G <| fun rs ->
v, rs
let bind (G t) uf : ResultGenerator<_> =
G <| fun rs ->
let tv, trs = t rs
let (G u) = uf tv
u trs
let combine (G t) (G u) : ResultGenerator<_> =
G <| fun rs ->
let _, trs = t rs
u trs
let direct v : ResultGenerator<_> =
G <| fun rs ->
(), Direct v::rs
let delayed d : ResultGenerator<_> =
G <| fun rs ->
(), Delayed d::rs
let trace m : ResultGenerator<_> =
G <| fun rs ->
(), Delayed (fun () -> printfn "%s" m)::rs
let tracef fmt = kprintf trace fmt
type Builder() =
class
member x.Bind (t, uf) = bind t uf
member x.Combine (t, u) = combine t u
member x.Return v = value v
member x.ReturnFrom t = t : ResultGenerator<_>
end
let run (G t) =
let v, rs = t []
v, List.rev rs
let builder = Builder ()
let test =
builder {
do! direct "Hello"
do! tracef "A trace:%s" "!"
do! direct "There"
return 123
}
[<EntryPoint>]
let main argv =
run test |> printfn "%A"
0