我理解Reader或Maybe或State monad是如何工作的,但是使用Continuations monad会遇到困难。 像下面这样的例子,吹我的脑袋
type ContinuationMonad() =
member this.Bind (m, f) = fun c -> m (fun a -> f a c)
member this.Return x = fun k -> k x
我认为我的问题是我无法获得Continuation的monadic类型(如Cont<' T>)以及我如何解包它并回收。 任何有用的示例或链接都非常感谢。
答案 0 :(得分:12)
我不会重复其他地方所说的内容 - 评论中提到的帖子提供了很多关于延续monad的细节。但有一点可能有用,就是使用Cont<'T>
的明确定义重写代码段:
type Cont<'T> =
Cont of (('T -> unit) -> unit)
类型Cont<'T>
表示计算。你可以通过给它一个函数'T -> unit
开始它来获取结果并对它做一些事情(比如打印它)。当你启动它时,它会返回unit
,它会(在某个时刻)产生一个值'T
并调用你提供的 continuation 。
使用这个更明确的定义,构建器可以定义为:
type ContinuationMonad() =
member this.Bind (ma, f) =
Cont(fun k ->
let (Cont ca) = ma
ca (fun a ->
let (Cont cb) = f a
cb k))
member this.Return x =
Cont(fun k -> k x)
Return
成员创建一个计算,当给定延续k
时,立即使用我们返回的值x
调用此延续。
Bind
成员返回一个新计算,当给定延续k
时,它开始由m
指定的计算;当此计算产生值a
时,它调用函数f
和然后调用f
返回的计算与原始延续k
(这是最终应该用最终结果调用的“最终”延续。)
答案 1 :(得分:1)
我发现Tomas的回答很有帮助,但有一些重要的警告:
使用名称Cont
来表示计算是恕我直言,令人困惑
因为它(错误地)表明Cont
是一个契约。一世
建议改为使用名称Inc
,因为它代表了一个
&#34;不完全&#34;计算。此计算包含一个值(类型
'T
)它已准备好传递给续集。 重点:
Inc
与延续不同。
我还建议在没有模式匹配的情况下定义Inc
歧视联盟的开销。这简化了实施
<{1}}的{{1}}。
我也不确定为什么我们应该总是假设一个延续
产生Bind
,但它确实大大简化了事情,所以
我将在下面的内容中保留这一假设。
因此,我们可以将延续定义为签名为unit
的任何函数,我们可以将'T -> unit
定义为:
Inc
在英语中,type Inc<'T> =
('T -> unit) -> unit
将其包裹的Inc
值传递给给定的延续函数,从而产生'T
。
接下来,我们需要unit
函数的显式签名。因为它是monad,我们知道它必须是这样的:
Bind
所以let bind (inc : Inc<'T>) (wrap : 'T -> Inc<'U>) : Inc<'U> =
采用不完整的计算(bind
类型)和一个可以&#34;包裹&#34;不完整计算中的原始值(类型为'T
),并返回该类型的新的不完整计算。基于此签名,我们知道'U
必须返回bind
,而Inc
是一个以延续为输入的函数。所以我们知道Inc
的实现必须从这样开始:
bind
我们的工作是提取包含在给定 fun (cont : 'U -> unit) -> ...
内的'T
值,然后使用给定的包装函数重新包装它。 主要洞察力:获取此值的唯一方法是让Inc
通过我们现在编写的续篇传递给我们!在这个&#34;人造&#34;继续,我们重新包装提取的值并将其返回给调用者。因此,完成的Inc
函数如下所示:
bind