【问题标题】:How does continuation monad really work延续单子如何真正起作用
【发布时间】:2017-02-24 10:08:33
【问题描述】:

我了解 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 的一元类型(如 Cont)以及如何打开它并返回。 非常感谢任何有用的示例或链接。

【问题讨论】:

  • 这在here,尤其是@C 中得到了详尽的介绍。 A. McCann 的回答值得关注(并且可能重读一两次)。

标签: f# functional-programming


【解决方案1】:

我发现 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

【讨论】:

    【解决方案2】:

    我不会重复其他地方已经说过的话 - cmets 中提到的帖子提供了很多关于 continuation monad 的细节。但可能有帮助的一件事是使用Cont&lt;'T&gt; 的显式定义重写您的代码 sn-p:

    type Cont<'T> = 
      Cont of (('T -> unit) -> unit)
    

    Cont&lt;'T&gt; 类型表示计算。您可以启动它,方法是给它一个函数'T -&gt; 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(这是“最终”延续最终应该以最终结果调用)。

    【讨论】:

    • 作为一个 monad 新手,这对我帮助很大。我在下面扩展了一点。
    猜你喜欢
    • 1970-01-01
    • 2010-10-14
    • 2016-06-22
    • 2018-03-23
    • 2010-09-07
    • 2017-02-08
    • 2017-06-22
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多