在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?

我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。


当前回答

除了上面出色的答案之外,让我为您提供以下文章的链接(由Patrick Thomson撰写),该文章通过将概念与JavaScript库jQuery(及其使用“方法链接”来操作DOM的方式)相关联来解释monads:jQuery是Monad

jQuery文档本身并没有提到术语“monad”,而是谈到了可能更熟悉的“构建器模式”。这并不能改变一个事实,那就是你有一个合适的monad,也许你甚至没有意识到它。

其他回答

但是,你本可以发明蒙纳斯!

sigfpe说:但所有这些都将单子介绍为需要解释的深奥的东西。但我想说的是,它们一点都不深奥。事实上,面对函数式编程中的各种问题,你会不可避免地被引向某些解决方案,所有这些都是单子的例子。事实上,如果你还没有发明,我希望你现在就发明它们。这是注意到所有这些解决方案实际上都是变相的相同解决方案的一小步。读完这篇文章后,你可能会更好地理解单子上的其他文档,因为你会发现你所看到的一切都是你已经发明的。monads试图解决的许多问题都与副作用有关。因此,我们将从它们开始。(请注意,monad让您做的不仅仅是处理副作用,特别是许多类型的容器对象都可以被视为monad。monad的一些介绍发现,很难协调monad的这两种不同用法,并且只关注其中一种。)在命令式编程语言(如C++)中,函数的行为与数学函数完全不同。例如,假设我们有一个C++函数,它接受一个浮点参数并返回一个浮点结果。从表面上看,它可能有点像一个将实数映射到实数的数学函数,但C++函数可以做的不仅仅是返回一个依赖于其参数的数字。它可以读取和写入全局变量的值,也可以将输出写入屏幕并接收用户的输入。然而,在纯函数语言中,函数只能读取在其参数中提供给它的内容,而它对世界产生影响的唯一方式是通过它返回的值。

monad是一种将共享共同上下文的计算组合在一起的方法。这就像建立一个管道网络。当构建网络时,没有数据流过它。但是当我用“bind”和“return”将所有位拼接在一起后,我调用类似runMyMonad monad数据的东西,数据流过管道。

解释monad似乎就像解释控制流语句一样。想象一下,一个非程序员要求你解释它们?

你可以给他们一个涉及理论的解释——布尔逻辑、寄存器值、指针、堆栈和框架。但那太疯狂了。

你可以用语法来解释它们。基本上,C中的所有控制流语句都有大括号,您可以通过它们相对于括号的位置来区分条件和条件代码。这可能更疯狂。

或者,您也可以解释循环、if语句、例程、子例程以及可能的协例程。

Monad可以取代相当多的编程技术。语言中有一种特定的语法支持它们,还有一些关于它们的理论。

它们也是函数式程序员使用命令式代码而不承认它的一种方式,但这并不是他们唯一的用途。

解释“什么是monad”有点像说“什么是数字?”我们总是使用数字。但想象一下,你遇到了一个对数字一无所知的人。你怎么解释数字是什么?你怎么开始描述为什么这可能有用?

什么是单子?简单的回答是:这是一种将操作链接在一起的特定方式。

本质上,您正在编写执行步骤,并将它们与“绑定函数”链接在一起。(在Haskell中,它名为>>=。)您可以自己编写对绑定运算符的调用,也可以使用语法糖,使编译器为您插入这些函数调用。但无论哪种方式,每个步骤都由对该绑定函数的调用分隔。

因此绑定函数就像分号;它将流程中的步骤分开。bind函数的任务是获取上一步的输出,并将其输入下一步。

听起来不太难,对吧?但单子不止一种。为什么?怎样

好吧,bind函数可以从一个步骤中获取结果,并将其传递给下一个步骤。但如果这就是单子的全部。。。这实际上不是很有用。理解这一点很重要:每个有用的monad除了做monad之外,还做其他事情。每一个有用的单子都有一种“特殊的力量”,这使它独一无二。

(没有什么特别作用的monad被称为“身份monad”。与身份函数类似,这听起来是一件毫无意义的事情,但事实证明并非如此……但这是另一回事™.)

基本上,每个monad都有自己的绑定函数实现。你可以编写一个绑定函数,这样它就可以在执行步骤之间做一些傻事。例如:

如果每个步骤都返回一个成功/失败指示符,则只有在前一个步骤成功的情况下,才能让绑定执行下一个步骤。这样,失败的步骤“自动”中止整个序列,而无需您进行任何条件测试。(故障单)扩展这个想法,您可以实现“异常”。(错误单点或异常单点。)因为您自己定义它们,而不是将其作为一种语言特性,所以您可以定义它们的工作方式。(例如,您可能希望忽略前两个异常,仅在引发第三个异常时中止。)您可以使每个步骤返回多个结果,并让bind函数对其进行循环,将每个结果输入到下一步。这样,在处理多个结果时,就不必一直到处写循环。绑定函数“自动”为您完成所有这些。(单子)除了将“结果”从一个步骤传递到另一个步骤之外,还可以让bind函数传递额外的数据。这些数据现在不会显示在源代码中,但您仍然可以从任何地方访问它,而无需手动将其传递给每个函数。(《读者》杂志)您可以这样做,以便可以替换“额外数据”。这允许您模拟破坏性更新,而无需实际执行破坏性更新。(莫纳德州及其堂弟作家莫纳德。)因为您只是在模拟破坏性更新,所以您可以轻松地完成真正的破坏性更新所无法完成的事情。例如,您可以撤消上一次更新,或恢复到旧版本。你可以制作一个可以暂停计算的monad,这样你就可以暂停你的程序,进入并修补内部状态数据,然后恢复它。您可以将“continuations”实现为monad。这可以让你打破人们的想法!

所有这些和更多的都可以通过monad实现。当然,这一切在没有单子的情况下也是完全可能的。使用monad非常简单。

我对monads还是个新手,但我想我会分享一个我觉得读起来很好的链接(带图片!!):http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/(无隶属关系)

基本上,我从这篇文章中得到的温暖而模糊的概念是monad基本上是适配器,允许不同的函数以可组合的方式工作,即能够将多个函数串起来并混合和匹配它们,而不用担心不一致的返回类型等。因此,当我们尝试制作这些适配器时,BIND函数负责将苹果与苹果、橙子与橙子放在一起。LIFT功能负责使用“较低级别”的功能,并将其“升级”为与BIND功能一起使用并可组合。

我希望我做得对,更重要的是,希望这篇文章对单子有一个有效的观点。如果没有别的话,这篇文章有助于激发我学习更多关于单子叶植物的欲望。