Cli*_*int 3 monads f# computation-expression deferred
我正在尝试研究如何使用计算构建器来表示延迟的嵌套步骤集.
到目前为止我有以下内容:
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"
}
}
Run Code Online (Sandbox Code Playgroud)
这产生了所需的结构:
A(B(C(c.1)), D)
Run Code Online (Sandbox Code Playgroud)
我的问题是,在构建结构时,会进行printfn调用.
理想情况下,我想要的是能够检索树结构,但能够调用一些返回的函数,然后执行内部块.
我意识到这意味着如果你有两个嵌套的步骤,它们之间有一些"普通"代码,它需要能够读取步骤声明,然后调用它(如果这有意义吗?).
我知道,Delay和Run有些事情在延迟执行用于计算表达式,但我不知道,如果他们帮助我在这里,因为他们不幸的是,一切评价.
我很可能错过了一些明显而且非常"功能性"的东西,但我似乎无法让它做到我想要的.
我正在使用它id进行演示,它们是这个难题的一部分,我想象我如何表达我想要的表达的"可调用的"部分.
正如在另一个答案中所提到的,免费monad为思考这类问题提供了一个有用的理论框架 - 但是,我认为你并不一定需要它们来回答你在这里提出的具体问题.
首先,我必须添加Return到您的计算构建器以使您的代码编译.因为你永远不会返回任何东西,我只是添加了一个过载unit,相当于Zero:
member this.Return( () ) = this.Zero()
Run Code Online (Sandbox Code Playgroud)
现在,回答你的问题 - 我认为你需要修改你的区别联合以允许延迟产生的计算Entry- 你确实unit -> unit在域模型中有函数,但这还不足以推迟产生新条目的计算.所以,我认为你需要扩展类型:
type Entry =
| Leaf of string * (unit -> unit)
| Node of string * Entry list * (unit -> unit)
| Delayed of (unit -> Entry)
Run Code Online (Sandbox Code Playgroud)
在评估时Entry,您现在需要处理Delayed案例 - 其中包含可能执行副作用的函数,例如打印"TEST".
现在,您可以添加Delay到您的计算建设者和也实现了丢失的情况下,Delayed在Bind这样的:
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()) )
Run Code Online (Sandbox Code Playgroud)
本质上,Bind将创建一个新的延迟计算,在调用时,它会评估条目,v直到它找到一个节点或一个叶子(折叠所有其他延迟的节点),然后执行与您之前的代码相同的操作.
我认为这回答了你的问题 - 但我在这里会有点小心.我认为计算表达式作为一种语法糖是有用的,但是如果你考虑它们而不是考虑你实际解决的问题领域,它们是非常有害的 - 在这个问题上,你没有多说你的实际问题.如果你这样做,答案可能会非常不同.