Abe*_*bel 12 monads f# haskell computation-expression continuation-passing
(后来的访问者:这个问题的两个答案都给出了很好的见解,如果你感兴趣的话,你应该把它们都读出来,我只能把它作为SO的限制除外)
从我在网上找到的关于延续monad的所有讨论,他们要么提到它如何与一些琐碎的例子一起使用,要么他们解释它是一个基本的构建块,因为在这篇关于所有monad的母亲的文章是延续monad.
我想知道是否有适用范围.我的意思是,在continuation monad中包装递归函数或相互递归是否有意义?它有助于提高可读性吗?
这是从这篇SO帖子中获取的延续模式的F#版本:
type ContinuationMonad() =
member this.Bind (m, f) = fun c -> m (fun a -> f a c)
member this.Return x = fun k -> k x
let cont = ContinuationMonad()
Run Code Online (Sandbox Code Playgroud)
仅仅是为了学术兴趣,例如帮助理解monad或计算构建者?或者是否存在一些真实的适用性,增加了类型安全性,还是避免了难以解决的典型编程问题?
即,来自Ryan Riley的call/cc的延续monad表明处理异常很复杂,但它没有解释它试图解决的问题,并且示例没有说明为什么它需要特定的monad.不可否认,我只是不明白它的作用,但它可能是一个宝库!
(注意:我对理解延续monad是如何工作不感兴趣,我认为我对它有一个很好的掌握,我只是看不出它解决了什么编程问题.)
"所有monad的母亲"的东西并不纯粹是学术性的.Dan Piponi引用了Andrzej Filinski的代表Monads,这是一篇相当不错的论文.它的结果是,如果您的语言具有分隔的延续(或者可以模仿它们call/cc和单个可变状态),那么您可以透明地向任何代码添加任何 monadic效果.换句话说,如果您具有分隔的延续而没有其他副作用,则可以实现(全局)可变状态或异常或回溯非确定性或协作并发.您可以通过定义一些简单的函数来完成其中的每一项.没有全球转型或任何需要.此外,您只需在使用时支付副作用.事实证明,Schemers完全正确call/cc地表达了高度的表现力.
如果你的语言没有分隔的延续,你可以通过延续monad(或者更好的双管延续monad)获得它们.当然,如果你打算以monadic风格写作 - 这是一个全球性的转变 - 为什么不从一开始就使用所需的monad呢?对于Haskellers,这通常是我们所做的,但是,在许多情况下使用continuation monad仍然有好处(虽然隐藏起来).一个很好的例子是Maybe/ Optionmonad就像有例外,除了只有一种类型的异常.基本上,这个monad捕获返回"错误代码"的模式,并在每次函数调用后检查它.这正是典型定义所做的,除了"函数调用",我的意思是计算的每个(monadic)步骤.可以说,这是非常低效的,特别是在绝大多数时间没有错误时.如果你反映Maybe到continuation monad,虽然你必须支付CPSed代码的成本(GHC Haskell处理得非常好),你只需要在重要的地方(即catch语句)检查"错误代码" .在Haskell中,Codensitymonad比danidiaz提到的是一个更好的选择,因为Haskellers想要的最后一件事是使它可以在代码中透明地交错任意效果.
正如danidiaz所提到的,使用基本上连续monad或某些变体更容易或更有效地实现许多monad.回溯搜索就是一个例子.虽然不是回溯中最新的东西,但我最喜欢使用它的一篇论文是Haskell中的Typed Logical Variables.其中使用的技术也用于有线硬件描述语言.同样来自Koen Claesson的是A Poor Man's Concurrency Monad.在这个例子中,对思想的更现代的用法包括:Haskell中的确定性并行性monad用于确定性并行的Monad和可扩展的I/O管理器,用于可扩展网络服务的事件和线程.我确信我可以找到Scala中使用的类似技术.如果未提供,则可以使用continuation monad在F#中实现异步工作流.事实上,Don Syme 引用了我刚引用的相同论文.如果你可以序列化函数但没有continuation,你可以使用continuation monad来获取它们,并进行像Seaside这样的系统流行的序列化延续类型的web编程.即使没有可序列化的延续,您也可以使用该模式(与async基本相同)至少避免回调,同时在本地存储延续并仅发送密钥.
最终,Haskellers之外的人很少使用任何容量的monad,正如我前面提到的,Haskellers倾向于使用比继续monad更多的可控制monad,尽管他们在内部使用它们相当多.然而,继续monad或continuation monad之类的东西,尤其是异步编程,变得不那么罕见了.随着C#,F#,Scala,Swift甚至Java开始采用支持monadic或至少monadic风格的编程,这些想法将得到更广泛的使用.如果节点开发人员更熟悉这一点,也许他们会意识到你可以拥有自己的蛋糕并且在事件驱动的编程方面也可以吃掉它.
为了提供更直接的F#特定答案(尽管Derek已经涵盖了这一点),延续monad几乎捕获了异步工作流如何工作的核心.
continuation monad是一个函数,当给定一个continuation时,最终用结果调用continuation(它可能永远不会调用它或者它也可能反复调用它):
type Cont<'T> = ('T -> unit) -> unit
Run Code Online (Sandbox Code Playgroud)
F#异步计算有点复杂 - 它们继续(在成功的情况下),异常和取消延续,并且还包括取消令牌.使用稍微简化的定义,F#核心库使用(请参阅此处的完整定义):
type AsyncParams =
{ token : CancellationToken
econt : exn -> unit
ccont : exn -> unit }
type Async<'T> = ('T -> unit) * AsyncParams -> unit
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,如果你忽略了AsyncParams它,它几乎就是延续monad.在F#中,我认为"经典"monad作为灵感而不是直接实现机制更有用.这里,continuation monad提供了一个如何处理某些类型的计算的有用模型 - 并且有许多额外的异步特定方面,核心思想可用于实现异步计算.
我认为这与monad在经典学术作品或Haskell中的使用方式有很大不同,在Haskell中,它们倾向于"按原样"使用,并且可能以各种方式组成,以构建捕捉更复杂行为的更复杂的monad.
这可能只是我个人的观点,但我会说延续monad本身并不实用,但它是一些非常实用的想法的基础.(就像lambda演算本身并不是真的有用,但它可以被看作是很好的实用语言的灵感!)
| 归档时间: |
|
| 查看次数: |
789 次 |
| 最近记录: |