我正在寻找一个可以表达以下内容的计算表达式:
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
}
并能够从x
中获取一个列表:
[| "Message 1"
"Message 2"
"Message 3"
"Message 4" |]
从未调用过printfn
,但具有以后执行它的能力(如果有意义)。
它不需要与do!
关键字一起使用,可以是yield
或return
,无论它需要工作什么。
换句话说,我希望能够在计算Express中收集某些状态,并将可以稍后执行的工作(printfn
)排入队列。
我已经尝试了一些方法,但是不确定是否有可能。
答案 0 :(得分:2)
从OP问题中找出精确的解决方案有点困难。相反,我将发布一些OP可能可以适应需求的代码。
我定义Result和ResultGenerator
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
生成器生成一个值以及直接值和延迟值的列表,直接值是上面的字符串列表,但与它们混合在一起的是延迟值。我喜欢混合在一起返回,这样可以保留顺序。
请注意,这是有时称为State
单子的版本。
除了bind
和构建器之类的CE类组件之外,我还创建了两个直接函数和延迟函数。
direct
用于创建直接值,而delayed
用于创建延迟值(具有函数)
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