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

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


当前回答

monad是用于封装状态变化的对象的东西。在不允许您具有可修改状态的语言(例如,Haskell)中最常遇到这种情况。

例如文件I/O。

您将能够使用文件I/O的monad来将不断变化的状态本质与使用monad的代码隔离开来。Monad内部的代码可以有效地忽略Monad外部世界的变化状态,这使您更容易理解程序的整体效果。

其他回答

对于来自命令式背景(c#)的人,

考虑以下代码

bool ReturnTrueorFalse(SomeObject input)
{
    if(input.Property1 is invalid)
    {
        return false;
    }

    if(input.Property2 is invalid)
    {
        return false;
    }

    DoSomething();
    return true;
}

您会看到很多这样的代码,甚至不会看到早期返回,但所有检查都是嵌套完成的。现在,Monad是一种模式,它可以像下面一样被压平

Monad<bool> ReturnTrueorFalse(SomeObject input) =>
    from isProperty1Valid in input.Property1
    from isProperty2Valid in input.Property2
    select Monad.Create(isProperty1Valid && isProperty2Valid);

这里有几点需要注意。首先,更改函数的返回值。其次,输入的两个财产都必须是Monad。接下来,Monad应该实现SelectMany(LINQ的展平运算符)。由于SelectMany是为该类型实现的,因此可以使用查询语法编写语句

那幺,什么是莫纳德?它是一种以可组合方式对返回相同类型的表达式进行扁平化的结构。这在函数式编程中特别有用,因为大多数函数式应用程序倾向于将状态和IO保持在应用程序的边缘层(例如:控制器),并在整个调用堆栈中返回基于Monad的返回值,直到需要解包该值。当我第一次看到这张照片时,我最大的优点是它很容易在眼睛上看到,也很有陈腔滥调。

每个c#(现在几乎每个人)开发人员都能立即识别的Monad的最佳示例是async/await。在.Net4.5之前,我们必须使用ContinueWith编写基于任务的语句来处理回调,在async/await之后,我们开始使用同步语法来处理异步语法。这是可能的,因为Task是一个“monad”。

关于OOP开发人员的详细说明,请参阅本文,这是一个简单的实现和语言文本,其中包含许多很棒的Monad和大量关于函数式编程的信息

monad是一个容器,但用于数据。一个特殊的容器。

所有容器都可以有开口、把手和喷口,但这些容器都保证有一定的开口、把手或喷口。

为什么?因为这些有保证的开口、把手和喷口对于以特定、常见的方式拾取和连接容器非常有用。

这使您可以选择不同的容器,而不必对它们了解太多。它还允许不同类型的容器轻松连接在一起。

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

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

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

monad是用于封装状态变化的对象的东西。在不允许您具有可修改状态的语言(例如,Haskell)中最常遇到这种情况。

例如文件I/O。

您将能够使用文件I/O的monad来将不断变化的状态本质与使用monad的代码隔离开来。Monad内部的代码可以有效地忽略Monad外部世界的变化状态,这使您更容易理解程序的整体效果。

我最喜欢的Monad教程:

http://www.haskell.org/haskellwiki/All_About_Monads

(在谷歌搜索“monad教程”的17万次点击中!)

@斯图:monads的目的是允许您将(通常)顺序语义添加到纯代码中;您甚至可以组合Monad(使用Monad Transformers)并获得更有趣和复杂的组合语义,例如,带有错误处理的解析、共享状态和日志记录。所有这些在纯代码中都是可能的,monad只允许您将其抽象并在模块化库中重用(在编程中总是很好的),并提供方便的语法使其看起来势在必行。

Haskell已经有了运算符重载[1]:它使用类型类的方式与使用Java或C#中的接口的方式非常相似,但Haskell恰好也允许使用非字母数字标记(如+&&和>)作为中缀标识符。如果您的意思是“重载分号”[2],那么在您看来这只是运算符重载。“重载分号”听起来像是黑魔法,自找麻烦(想象一下有进取心的Perl黑客听到了这个想法),但关键是没有monad就没有分号,因为纯函数代码不需要或不允许显式排序。

这一切听起来比实际情况要复杂得多。sigfpe的文章很酷,但使用了Haskell来解释它,这有点无法打破理解Haskell到grok Monads和理解Monads到grok Haskell的鸡和蛋的问题。

[1] 这是与monad不同的问题,但monad使用Haskell的运算符重载特性。

[2] 这也是一个过度简化,因为链接一元操作的运算符是>>=(发音为“bind”),但有语法糖(“do”)允许您使用大括号和分号和/或缩进和换行。