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

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


当前回答

世界需要的是另一篇monad博客文章,但我认为这对识别野外现存的monad很有用。

单子是分形

上面是一个叫做Sierpinski三角形的分形,这是我唯一记得画的分形。分形是与上述三角形相似的自相似结构,其中部分与整体相似(在这种情况下,正好是母三角形比例的一半)。单子是分形。给定一个一元数据结构,它的值可以组合成数据结构的另一个值。这就是为什么它对编程有用,这也是为什么它在许多情况下都会出现。

其他回答

Monad用于控制流,就像抽象数据类型用于数据一样。

换句话说,许多开发人员对集合、列表、字典(或哈希、或地图)和树的概念很熟悉。在这些数据类型中有许多特殊情况(例如InsertionOrderPreservingIdentityHashMap)。

然而,当面对程序“流”时,许多开发人员还没有接触到比if、switch/case、do、while、goto(grr)和(可能)闭包更多的构造。

因此,monad只是一个控制流构造。替代monad的更好短语是“控制类型”。

因此,monad具有用于控制逻辑、语句或函数的槽——数据结构中的等价物是,某些数据结构允许您添加数据,并删除数据。

例如,“if”monad:

if( clause ) then block

最简单的是有两个槽:一个子句和一个块。if monad通常用于评估子句的结果,如果不是false,则评估块。许多开发人员在学习“如果”时并没有接触到monad,而且编写有效的逻辑并不需要理解monad。

monad可能会变得更复杂,就像数据结构可能变得更复杂一样,但monad有很多大类可能具有相似的语义,但实现和语法不同。

当然,数据结构可以在单子上迭代或遍历,也可以以同样的方式进行评估。

编译器可能支持也可能不支持用户定义的monad。哈斯克尔当然知道。Ioke有一些类似的功能,尽管语言中没有使用monad一词。

http://mikehadlow.blogspot.com/2011/02/monads-in-c-8-video-of-my-ddd9-monad.html

这是你要找的视频。

用C#演示组合和对齐类型的问题,然后用C#正确实现它们。最后,他展示了F#和Haskell中相同的C#代码的外观。

(另请参见“什么是monad?”中的答案)

蒙纳斯的一个很好的动机是西格菲(丹·皮波尼)的《你本可以发明蒙纳斯!(也许你已经有了)。还有很多其他monad教程,其中许多都试图使用各种类比以“简单的术语”来解释monad:这就是monad教程谬论;避开它们。

正如MacIver博士在《告诉我们为什么你的语言很糟糕》中所说:所以,我讨厌Haskell的事情:让我们从显而易见的开始。Monad教程。不,不是单子。特别是教程。他们没完没了,夸夸其谈,亲爱的上帝,他们太乏味了。此外,我从未见过任何令人信服的证据表明它们确实有帮助。阅读类定义,编写一些代码,忘掉这个可怕的名字。

你说你懂“也许莫纳德”吗?很好,你在路上了。只要开始使用其他monad,迟早你会了解monad的一般含义。

(如果你以数学为导向,你可能想忽略几十个教程,学习定义,或遵循类别理论的讲座:)定义的主要部分是Monad M包含一个“类型构造器”,为每个现有类型“T”定义一个新类型“M T”,以及在“常规”类型和“M”类型之间来回移动的一些方式。]

同样,令人惊讶的是,对monad最好的介绍之一实际上是介绍monad的早期学术论文之一,Philip Wadler的Monad for functional programming。它实际上有一些实用的、非平凡的激励性例子,与许多人工教程不同。

遵循您简短、简洁、实用的指示:

理解monad最简单的方法是在上下文中应用/组合函数。假设你有两个计算,它们都可以看作是两个数学函数f和g。

f取一个String并生成另一个String(取前两个字母)g获取一个String并生成另一个String(大写转换)

因此,在任何语言中,“取前两个字母并将其转换为大写”的转换都会写成g(f(“某个字符串”))。因此,在纯完美函数的世界中,合成只是:先做一件事,然后再做另一件事。

但假设我们生活在一个功能可能失败的世界中。例如:输入字符串可能有一个字符长,因此f将失败。所以在这种情况下

f获取一个String并生成一个String或Nothing。g仅在f未失败时生成字符串。否则,将不生成任何内容

所以现在,g(f(“somestring”))需要一些额外的检查:“计算f,如果它失败,那么g应该返回Nothing,否则计算g”

此思想可应用于任何参数化类型,如下所示:

让Context[Sometype]是Context中Sometype的计算。考虑功能

f: :AnyType->上下文[Sometype]g: :某些类型->上下文[AnyOtherType]

合成g(f())应该读作“compute f。在这个上下文中,做一些额外的计算,然后计算g,如果它在上下文中有意义”

我将尝试在Haskell的背景下解释Monad。

在函数式编程中,函数组合很重要。它允许我们的程序由小的、易于阅读的函数组成。

假设我们有两个函数:g::Int->String和f::String->Bool。

我们可以做(f.g)x,这与f(gx)相同,其中x是Int值。

当进行合成/将一个函数的结果应用到另一个函数时,使类型匹配是很重要的。在上述情况下,g返回的结果类型必须与f接受的类型相同。

但有时值是在上下文中的,这使得排列类型有点不容易。(在上下文中设置值非常有用。例如,Maybe Int类型表示可能不存在的Int值,IO String类型表示由于执行某些副作用而存在的String值。)

假设我们现在有g1::Int->Maybe String和f1::String->Maybe Bool。g1和f1分别与g和f非常相似。

我们不能做(f1.g1)x或f1(g1 x),其中x是Int值。g1返回的结果类型不是f1期望的类型。

我们可以用。运算符,但现在我们不能用..组合f1和g1。。问题是我们不能直接将上下文中的值传递给期望值不在上下文中的函数。

如果我们引入一个运算符来组合g1和f1,这样我们就可以写出(f1 operator g1)x,这不是很好吗?g1返回上下文中的值。该值将脱离上下文并应用于f1。是的,我们有这样一个操作员。它是<=<。

我们还有一个>>=运算符,它为我们做了完全相同的事情,尽管语法略有不同。

我们写:g1 x>>=f1。g1 x是Maybe Int值。>>=运算符帮助将Int值从“可能不存在”上下文中取出,并将其应用于f1。f1的结果是Maybe Bool,它将是整个>>=操作的结果。

最后,为什么Monad有用?因为Monad是定义>>=运算符的类型类,与定义==和/=运算符的Eq类型类非常相似。

总之,Monad类型类定义了>>=运算符,该运算符允许我们将上下文中的值(我们称为这些monadic值)传递给不需要上下文中值的函数。将考虑上下文。

如果这里需要记住一点,那就是Monads允许在上下文中包含值的函数组合。