用于构建状态和延迟执行的 F# 计算表达式
F# Computation Expression to build state and defer execution
我正在寻找可以表达以下内容的计算表达式:
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
,只要它起作用就可以。
换句话说,我希望能够在计算快车中收集一些状态,并排队稍后可以执行的工作(printfn
s)。
我已经尝试了一些方法,但不确定是否可行。
从OP问题中找出一个精确的解决方案有点困难。相反,我将 post OP 可能可以根据需要调整的一些代码。
我定义了Result和ResultGenerator
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
生成器产生一个值和一个直接值和延迟值列表,直接值是上面的字符串列表,但与它们混合的是延迟值。我喜欢混合返回,以便保留顺序。
请注意,这是有时称为 State
monad 的版本。
除了 class CE 组件,如 bind
和 Builders,我还直接创建了两个函数和延迟函数。
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"])
(Delayed会在执行时打印trace)
希望这可以提供一些关于如何解决实际问题的想法。
完整来源:
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
我正在寻找可以表达以下内容的计算表达式:
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
,只要它起作用就可以。
换句话说,我希望能够在计算快车中收集一些状态,并排队稍后可以执行的工作(printfn
s)。
我已经尝试了一些方法,但不确定是否可行。
从OP问题中找出一个精确的解决方案有点困难。相反,我将 post OP 可能可以根据需要调整的一些代码。
我定义了Result和ResultGenerator
type Result =
| Direct of string
| Delayed of (unit -> unit)
type ResultGenerator<'T> = G of (Result list -> 'T*Result list )
生成器产生一个值和一个直接值和延迟值列表,直接值是上面的字符串列表,但与它们混合的是延迟值。我喜欢混合返回,以便保留顺序。
请注意,这是有时称为 State
monad 的版本。
除了 class CE 组件,如 bind
和 Builders,我还直接创建了两个函数和延迟函数。
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"])
(Delayed会在执行时打印trace)
希望这可以提供一些关于如何解决实际问题的想法。
完整来源:
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