延续monad如何真正起作用

时间:2016-10-14 21:26:47

标签: f# functional-programming

我理解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>)以及我如何解包它并回收。 任何有用的示例或链接都非常感谢。

2 个答案:

答案 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