我正在尝试研究如何使用计算构建器来表示一组延迟的嵌套步骤。
到目前为止,我得到了以下内容:
type Entry =
| Leaf of string * (unit -> unit)
| Node of string * Entry list * (unit -> unit)
type StepBuilder(desc:string) =
member this.Zero() = Leaf(desc,id)
member this.Bind(v:string, f:unit->string) =
Node(f(), [Leaf(v,id)], id)
member this.Bind(v:Entry, f:unit->Entry) =
match f() with
| Node(label,children,a) -> Node(label, v :: children, a)
| Leaf(label,a) -> Node(label, [v], a)
let step desc = StepBuilder(desc)
let a = step "a" {
do! step "b" {
do! step "c" {
do! step "c.1" {
// todo: this still evals as it goes; need to find a way to defer
// the inner contents...
printfn "TEST"
}
}
}
do! step "d" {
printfn "d"
}
}
这会产生所需的结构:
A(B(C(c.1)), D)
我的问题是,在构建结构时,会进行 printfn
调用。
理想情况下,我想要的是能够检索树结构,但能够调用一些返回的函数,然后执行内部 block 。
我意识到这意味着如果你有两个嵌套的步骤,它们之间有一些“正常”代码,它需要能够读取步骤声明,然后调用它(如果这有意义的话?)。
我知道 Delay
和 Run
是用于计算表达式的延迟执行的东西,但我不确定它们是否对我有帮助,因为不幸的是评估一切。
我很可能遗漏了一些非常明显且非常“实用”的东西,但我似乎无法让它做我想做的事情。
澄清
我正在使用 id
进行演示,它们是拼图的一部分,我想像我可以如何呈现我想要的表达式的“可调用”部分。
最佳答案
正如另一个答案中提到的,free monads 为思考这类问题提供了一个有用的理论框架 - 但是,我认为您不一定需要它们来获得特定问题的答案你在这里问。
首先,我必须将 Return
添加到您的计算构建器中以使您的代码编译。因为你从不返回任何东西,我只是添加了一个重载 unit
相当于 Zero
:
member this.Return( () ) = this.Zero()
现在,回答你的问题 - 我认为你需要修改你的有区别的联合以允许延迟产生 Entry
的计算 - 你确实有函数 unit -> unit
在域模型中,但这还不足以延迟将产生新条目的计算。所以,我认为你需要扩展类型:
type Entry =
| Leaf of string * (unit -> unit)
| Node of string * Entry list * (unit -> unit)
| Delayed of (unit -> Entry)
当您评估 Entry
时,您现在需要处理 Delayed
案例 - 它包含一个可能执行副作用的函数,例如打印“TEST”。
现在您可以将 Delay
添加到您的计算构建器中,并在 Bind
中实现 Delayed
的缺失案例,如下所示:
member this.Delay(f) = Delayed(f)
member this.Bind(v:Entry, f:unit->Entry) = Delayed(fun () ->
let rec loop = function
| Delayed f -> loop (f())
| Node(label,children,a) -> Node(label, v :: children, a)
| Leaf(label,a) -> Node(label, [v], a)
loop (f()) )
本质上,Bind
将创建一个新的延迟计算,当调用该计算时,它会评估条目 v
直到找到一个节点或叶子(折叠所有其他延迟节点)然后做和你的代码之前做的一样的事情。
我认为这回答了你的问题 - 但我会在这里小心一点。我认为计算表达式作为语法糖很有用,但是如果您考虑它们而不是考虑实际解决的问题的领域,它们是非常有害的 - 在这个问题中,您没有对您的实际问题说太多.如果你这样做了,答案可能会大不相同。
关于f# - 部分延迟计算构建器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45684743/