f# - F# Computation Expression to build state and defer execution
问题描述
I am looking to build a computation expression where I can express the following:
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
}
and be able to take from x
a list:
[| "Message 1"
"Message 2"
"Message 3"
"Message 4" |]
without printfn
ever getting called, but with the ability to later execute it (if that makes sense).
It doesn't need to be with the do!
keyword, it could be yield
or return
, whatever is required for it to work.
To put it another way, I want to be able to collect some state in a computation express, and queue up work (the printfn
s) that can be executed later.
I have tried a few things, but am not sure it's possible.
解决方案
从 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 的版本。
除了像bind
Builders 这样的类 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
推荐阅读
- python - Tensorflow 在每个 epoch 结束时卡住了几秒钟
- java - Spring Swagger:在 POST 方法中的 @RequestBody 中传递实体时,Swagger 无法识别控制器
- sql - 在 SQL Presto 中将科学计数法转换为十进制
- typescript - 如何将具有特定功能的对象定义为类型或接口?
- spring-boot - 使用 Gradle 插件管理中的 BOM
- python - 如何在立方体内创建坐标数组?
- python - Django 模型强制大写 db_column 名称
- ddos - 执行拒绝服务攻击
- python - 如何在特定字符串之间分隔文本,然后将其转换为数据框?
- javascript - 无法在按钮单击时启动 javascript 警报