我发现 Tomas 的回答很有帮助,但有一些重要的警告:
使用名称 Cont 来表示计算在恕我直言是令人困惑的
因为它暗示(错误地)Cont 是一个延续。一世
建议改用名称Inc,因为它代表一个
“不完整”的计算。此计算包含一个值(类型
'T),它已准备好传递给延续。 重点:
Inc 与延续不同。
我还建议在没有模式匹配的情况下定义 Inc
有区别的联合的开销。这简化了实现
Bind 相当大。
我也不确定为什么我们应该假设一个延续总是
产生unit,但它确实大大简化了事情,所以
我将在下面保留这个假设。
因此,我们可以将延续定义为签名为'T -> unit 的任何函数,我们可以将Inc 定义为:
type Inc<'T> =
('T -> unit) -> unit
在英语中,Inc 将其包装的'T 值传递给给定的延续函数,从而产生unit。
接下来,我们需要Bind 函数的显式签名。因为它是一个 monad,所以我们知道它必须看起来像这样:
let bind (inc : Inc<'T>) (wrap : 'T -> Inc<'U>) : Inc<'U> =
所以bind 接受一个不完整的计算('T 类型)和一个可以在不完整计算('U 类型)中“包装”原始值的函数,并返回该类型的新的不完整计算.根据这个签名,我们知道bind 必须返回一个Inc,而Inc 是一个将延续作为输入的函数。所以我们知道bind 的实现必须这样开始:
fun (cont : 'U -> unit) -> ...
我们的工作是提取包装在给定Inc 中的'T 值,然后使用给定的包装函数重新包装它。 关键见解:获得此值的唯一方法是要求Inc 通过我们现在将编写的延续将其传递给我们!在这个“人造”延续中,我们重新包装提取的值并将其返回给调用者。因此,完成的bind 函数如下所示:
let bind (inc : Inc<'T>) (wrap : 'T -> Inc<'U>) : Inc<'U> =
fun (cont : 'U -> unit) -> // return an Inc, which is a function that takes a continuation as input
inc (fun t -> // force the incomplete computation to cough up its wrapped value
(wrap t) cont) // wrap the raw value so it can be sent to the given continuation