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

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


当前回答

http://code.google.com/p/monad-tutorial/正是为了解决这个问题而进行的工作。

其他回答

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

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一词。

解释

当用C#/Java术语解释时,这很简单:

monad是一个接受参数并返回特殊类型的函数。这个monad返回的特殊类型也称为monad。(monad是#1和#2的组合)有一些语法糖可以使调用此函数和类型转换更容易。

实例

monad有助于使函数式程序员的生活更轻松。典型示例:Maye monad接受两个参数,一个值和一个函数。如果传递的值为null,则返回null。否则它将计算函数。如果我们需要一个特殊的返回类型,我们也可以调用这个返回类型Maybe。一个非常粗糙的实现如下所示:

object Maybe(object value, Func<object,object> function)
{
    if(value==null)
        return null;

    return function(value);
}

这在C#中是非常无用的,因为这种语言缺乏使monad有用所需的语法糖。但是monad允许您用函数式编程语言编写更简洁的代码。

通常程序员在链中调用monad,如下所示:

var x = Maybe(x, x2 => Maybe(y, y2 => Add(x2, y2)));

在本例中,只有当x和y都为非null时,才会调用Add方法,否则将返回null。

答复

回答最初的问题:monad是一个函数和一个类型。就像一个特殊接口的实现。

让下面的“{|a|m}”表示一些一元数据。宣传以下内容的数据类型:

        (I got an a!)
          /        
    {| a |m}

函数f知道如何创建monad,只要它有一个a:

       (Hi f! What should I be?)
                      /
(You?. Oh, you'll be /
 that data there.)  /
 /                 /  (I got a b.)
|    --------------      |
|  /                     |
f a                      |
  |--later->       {| b |m}

在这里,我们看到函数f试图评估monad,但遭到了谴责。

(Hmm, how do I get that a?)
 o       (Get lost buddy.
o         Wrong type.)
o       /
f {| a |m}

函数f通过使用>>=找到提取a的方法。

        (Muaahaha. How you 
         like me now!?)       
    (Better.)      \
        |     (Give me that a.)
(Fine, well ok.)    |
         \          |
   {| a |m}   >>=   f

殊不知,monad和>>=勾结在一起。

            (Yah got an a for me?)       
(Yeah, but hey    | 
 listen. I got    |
 something to     |
 tell you first   |
 ...)   \        /
         |      /
   {| a |m}   >>=   f

但他们实际上在谈论什么?嗯,这取决于单子。仅仅抽象地谈论用处有限;你必须对特定的单子有一些经验,才能充实理解。

例如,数据类型Maybe

 data Maybe a = Nothing | Just a

有一个monad实例,其行为如下。。。

其中,如果情况只是

            (Yah what is it?)       
(... hm? Oh,      |
forget about it.  |
Hey a, yr up.)    | 
            \     |
(Evaluation  \    |
time already? \   |
Hows my hair?) |  |
      |       /   |
      |  (It's    |
      |  fine.)  /
      |   /     /    
   {| a |m}   >>=   f

但对于Nothing的情况

        (Yah what is it?)       
(... There      |
is no a. )      |
  |        (No a?)
(No a.)         |
  |        (Ok, I'll deal
  |         with this.)
   \            |
    \      (Hey f, get lost.) 
     \          |   ( Where's my a? 
      \         |     I evaluate a)
       \    (Not any more  |
        \    you don't.    |
         |   We're returning
         |   Nothing.)   /
         |      |       /
         |      |      /
         |      |     /
   {| a |m}   >>=   f      (I got a b.)
                    |  (This is   \
                    |   such a     \
                    |   sham.) o o  \
                    |               o|
                    |--later-> {| b |m}

因此,如果Maye monad实际上包含它所宣传的a,则它允许计算继续,但如果不包含,则中止计算。然而,结果仍然是一段单元数据,尽管不是f的输出。因此,Maye monad用于表示失败的上下文。

不同的单子叶植物表现不同。列表是具有一元实例的其他类型的数据。它们的行为如下:

(Ok, here's your a. Well, its
 a bunch of them, actually.)
  |
  |    (Thanks, no problem. Ok
  |     f, here you go, an a.)
  |       |
  |       |        (Thank's. See
  |       |         you later.)
  |  (Whoa. Hold up f,      |
  |   I got another         |
  |   a for you.)           |
  |       |      (What? No, sorry.
  |       |       Can't do it. I 
  |       |       have my hands full
  |       |       with all these "b" 
  |       |       I just made.) 
  |  (I'll hold those,      |
  |   you take this, and   /
  |   come back for more  /
  |   when you're done   / 
  |   and we'll do it   / 
  |   again.)          /
   \      |  ( Uhhh. All right.)
    \     |       /    
     \    \      /
{| a |m}   >>=  f  

在这种情况下,该函数知道如何从其输入生成列表,但不知道如何处理额外的输入和额外的列表。bind>>=,通过组合多个输出帮助f。我通过这个例子来说明,当>>=负责提取a时,它也可以访问f的最终绑定输出。事实上,除非它知道最终输出具有相同类型的上下文,否则它永远不会提取任何a。

还有其他monad用于表示不同的上下文。下面是一些其他特征。IO monad实际上没有a,但它认识一个人,会为你拿到a。州立大学圣莫尼德分校有一个秘密的圣莫尼德,它会把圣莫尼德藏在桌子下面给f,尽管f只是来要求一个a。

所有这一切的关键是,任何类型的数据如果声明自己是Monad,都会声明某种上下文来从Monad中提取值。从这一切中获得的巨大收益?好吧,用某种上下文来进行计算是很容易的。然而,当将多个上下文负载的计算串联在一起时,可能会变得混乱。monad操作负责解决上下文的交互,因此程序员不必这样做。

注意,>>=的使用通过从f中移除一些自主权来缓解混乱。也就是说,例如,在上面的Nothing情况下,f不再能够决定在Nothing的情况下要做什么;它被编码为>>=。这就是权衡。如果f有必要决定在Nothing的情况下做什么,那么f应该是从Maybe a到Maybe b的函数。在这种情况下,也许是monad是无关紧要的。

然而,请注意,有时数据类型不会导出它的构造函数(看看你的IO),如果我们想使用广告值,我们别无选择,只能使用它的monadic接口。

如果我理解正确的话,IEnumerable是从monad派生出来的。我想知道,对于我们这些来自C#世界的人来说,这可能是一个有趣的视角吗?

值得一提的是,这里有一些帮助我的教程链接(不,我还不知道单子是什么)。

http://osteele.com/archives/2007/12/overloading-semicolonhttp://spbhug.folding-maps.org/wiki/MonadsEnhttp://www.loria.fr/~kow/monads/