“所有单子的母亲”并不纯粹是学术性的。丹·皮波尼引用了安德烈·菲林斯基的
《表示单子》,这是一篇相当好的论文。其要点是,如果你的语言具有分界续体(或可以通过
call/cc
和单个可变状态模仿它们),那么你可以在任何代码中
透明地添加
任何单子效应。换句话说,如果你有分界续体
而且没有其他副作用,你可以
实现(全局)可变状态、异常、回溯非确定性或协作并发。你只需要定义一些简单的函数就可以做到这些。不需要进行全局转换或任何其他操作。而且,只有在使用副作用时才需要付出代价。结果表明,Schemers对于
call/cc
非常具有表现力的看法是完全正确的。
如果你的语言没有分隔的续延,你可以通过续延单子(或更好的双枪续延单子)来获得它们。当然,如果你无论如何都要以单子风格编写代码,这是一种全局转换,为什么不从一开始就使用所需的单子呢?对于Haskeller来说,这通常是我们所做的,但在许多情况下使用续延单子仍然有好处(尽管隐藏起来)。一个很好的例子是
Maybe
/
Option
单子,它类似于有异常,除了只有一种类型的异常。基本上,这个单子捕获了返回一个“错误码”并在每个函数调用后检查它的模式。这正是典型定义所做的,只不过我所说的“函数调用”是指计算的每个(单子)步骤。可以说,在大多数情况下没有错误时,这相当低效。但是,如果你将
Maybe
反映到续延单子中,虽然你必须支付CPSed代码的成本(GHC Haskell处理得非常好),但你只需要在重要的地方检查“错误码”,即
catch
语句。在Haskell中,danidiaz提到的
Codensity
单子是一个更好的选择,因为Haskeller最不想做的就是使任意效果可以透明地交错在他们的代码中。
正如danidiaz所提到的,许多单子更容易或更有效地使用基本上是一个连续单子或某个变体来实现。回溯搜索就是一个例子。虽然回溯不是最新的技术,但我最喜欢的一篇论文
Typed Logical Variables in Haskell中使用了它。其中使用的技术也被用于Wired Hardware Description Language中。Koen Claesson还有
A Poor Man's Concurrency Monad。这个例子中的思想的更现代化的应用包括:Haskell中用于确定性并行性的单子
A Monad for Deterministic Parallelism和可扩展的I/O管理器
Combining Events And Threads For Scalable Network Services。我相信我可以找到在Scala中使用类似技术的例子。如果没有提供,您可以使用连续单子在F#中实现异步工作流程。事实上,Don Syme
references引用了我刚刚引用的同样的论文。如果您可以序列化函数但没有连续,则可以使用连续单子来获得它们,并执行由系统(例如
Seaside)推广的序列化连续类型的Web编程。即使没有可序列化的连续性,您也可以使用该模式(本质上与async相同)来避免回调,同时在本地存储连续性并仅发送密钥。
最终,除了Haskellers以外,相对较少的人在任何情况下都在使用monads。正如我之前所说,Haskellers倾向于使用更可控的monads而不是continuation monad,尽管他们在内部经常使用它们。然而,continuation monads或类似continuation monad的东西,特别是用于异步编程,正在变得越来越常见。随着C#,F#,Scala,Swift甚至Java开始支持monadic或至少monadic-style编程,这些想法将变得更广泛使用。如果Node开发人员更熟悉此技术,也许他们会意识到在事件驱动编程方面可以兼顾两全。
fmap
操作收集到一个中,或将任意类型“升级”为Functor
)。我个人不知道Cont
和ContT
在实际中如何使用,但这反映了我的知识限制而不是它们使用的限制。 - dfeuer