Monad用简单的英语? (对于没有FP背景的OOP程序员)

就OOP程序员而言(没有任何函数式编程背景),什么是monad?

它解决了什么问题,最常用的地方是什么?

编辑:

为了澄清我正在寻找的那种理解,我们假设你将一个有monad的FP应用程序转换成一个OOP应用程序。 你会做什么来将monad的责任移交给OOP应用程序?

更新:这个问题是一个非常长的博客系列的主题,你可以在Monad读 – 谢谢你的伟大的问题!

就OOP程序员而言(没有任何函数式编程背景),什么是monad?

monad是一种“放大器”,遵循某些规则提供某些操作

首先,什么是“types放大器”? 我的意思是有些系统可以让你把一个types变成一个更特殊的types。 例如,在C#中考虑Nullable<T> 。 这是一个types的放大器。 它可以让你接受一个types,比如说int ,然后给这个types添加一个新的能力,也就是说,现在它不能在之前是空的。

作为第二个例子,考虑IEnumerable<T> 。 这是一个types的放大器。 它可以让你获取一个types,比如string ,并为这个types添加一个新的function,即你现在可以从任意数量的单个string中创build一个string序列。

什么是“某些规则”? 简而言之,基本types的函数有一个合理的方式来处理放大的types,使得它们遵循function组合的正常规则。 例如,如果你有一个整数函数,说

 int M(int x) { return x + N(x * 2); } 

那么Nullable<int>上的相应函数可以使所有在那里的操作符和调用一起“以同样的方式”一起工作。

(这是非常模糊和不精确的;你要求一个解释,没有任何关于function组成的知识。)

什么是“操作”?

  1. 有一种方法可以将非放大types的值转化为放大types的值。
  2. 有一种方法可以将非放大types的操作转换为放大types的操作,该types遵循前面提到的function组合的规则
  3. 通常有一种方法可以将未放大的types从放大types中取出。 (最后一点对于monad来说不是必须的,但是这种操作往往是存在的。)

再次,以Nullable<T>为例。 您可以使用构造函数将int转换为Nullable<int> 。 C#编译器会为您处理大多数可空的“提升”,但如果不提升,则提升转换非常简单:一种操作,

 int M(int x) { whatever } 

被转化成

 Nullable<int> M(Nullable<int> x) { if (x == null) return null; else return new Nullable<int>(whatever); } 

将一个可为空的int转换回int是通过Value属性完成的。

这是关键的function转换。 请注意,可转换操作的实际语义(空操作传播null)的实际语义是在转换中捕获的。 我们可以概括这一点。 假设你有一个从int到int的函数,就像我们原来的M.你可以很容易地把它变成一个函数,它接受一个int并返回一个Nullable<int>因为你可以通过可空构造函数运行结果。 现在假设你有这个高阶方法:

 Nullable<T> Bind<T>(Nullable<T> amplified, Func<T, Nullable<T>> func) { if (amplified == null) return null; else return func(amplified.Value); } 

看看你能用这个做什么? 任何接受一个int并返回一个int,或者接受一个int并返回一个可为null的int的方法现在可以应用可空的语义

此外:假设你有两种方法

 Nullable<int> X(int q) { ... } Nullable<int> Y(int r) { ... } 

你想组成它们:

 Nullable<int> Z(int s) { return X(Y(s)); } 

也就是说,Z是X和Y的组合,但是你不能这样做,因为X取int,Y返回一个可为null的int。 但是既然你有“绑定”操作,你可以做这个工作:

 Nullable<int> Z(int s) { return Bind(Y(s), X); } 

monad上的绑定操作是使放大types的函数组合起作用的。 我上面交涉的“规则”是monad保留了正常函数组成的规则, 与身份函数组成的结果是原始函数,该组合是关联的,等等。

在C#中,“绑定”被称为“SelectMany”。 看看它是如何在序列monad上工作的。 我们需要有三件事情:把一个值转换成一个序列,把一个序列转换成一个值,然后绑定序列上的操作。 这些操作是:

 IEnumerable<T> MakeSequence<T>(T item) { yield return item; } T Single<T>(IEnumerable<T> sequence) { // let's just take the first one foreach(T item in sequence) return item; } IEnumerable<T> SelectMany<T>(IEnumerable<T> seq, Func<T, IEnumerable<T>> func) { foreach(T item in seq) foreach(T result in func(item)) yield return result; } 

可空的单子规则是“将两个产生空白的函数结合在一起,检查内部结果是否为null;如果是,则产生null,如果不是,则用结果调用外部函数。 这是可空的期望的语义。 序列monad规则是“将两个产生序列的函数组合在一起,将外部函数应用于由内部函数产生的每个元素,然后将所有得到的序列连接在一起”。 单元的基本语义在Bind / SelectMany方法中被捕获; 这是告诉你monad是什么意思的方法

我们可以做得更好。 假设你有一个ints序列,并且有一个方法可以获取整数并得到string序列。 我们可以概括一下绑定操作,只要input的数据与另一个数据的输出相匹配,就可以构成能够返回不同放大types的函数:

 IEnumerable<U> SelectMany<T,U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> func) { foreach(T item in seq) foreach(U result in func(item)) yield return result; } 

所以现在我们可以说“把这些单独的整数放到一个整数序列中,把这个整数转换成一串string,放大到一串string,现在把这两个操作放在一起:把这一串整数放到所有的string序列“。 Monads允许你撰写你的扩音。

它解决了什么问题,最常用的地方是什么?

这就像是问“单身模式解决了什么问题?”,但我会试一试。

Monads通常用于解决如下问题:

  • 我需要为这种types创build新的function,并且仍旧结合这种types的旧function来使用新的function。
  • 我需要捕获一些types的操作,并将这些操作表示为可组合的对象,构build更大更大的构图,直到我只有正确的一系列操作为代表,然后我需要开始从事物中获得结果
  • 我需要用一种不利于副作用的语言清楚地expression副作用

(请注意,这些基本上有三种说法。

C#在其devise中使用monad。 正如已经提到的那样,可空模式非常类似于“可能是单子”。 LINQ完全是由monad构build的, “SelectMany”方法是什么操作组成的语义工作。 (Erik Meijer喜欢指出每个LINQ函数实际上可以由SelectMany实现,其他的只是一个方便。)

为了澄清我正在寻找的那种理解,我们假设你将一个有monad的FP应用程序转换成一个OOP应用程序。 你会怎么做monads的责任到OOP应用程序?

大多数OOP语言没有足够丰富的types系统来直接表示monad模式本身; 你需要一个支持types比generics更高的types系统。 所以我不会这么做 相反,我将实现代表每个monad的genericstypes,并实现代表您需要的三个操作的方法:将值转换为放大的值,将放大的值转换为值,并将未放大的值上的函数转换为函数放大的价值。

一个好的开始就是我们如何在C#中实现LINQ。 研究SelectMany方法; 了解序列monad如何在C#中工作是关键。 这是一个非常简单的方法,但非常强大!

为了更加深入理论地解释C#中的monad,我强烈推荐我的同事Wes Dyer关于这个主题的文章。 这篇文章是他们最终为我“点击”时向我解释的monads。

Monads的奇迹

为什么我们需要monads?

  1. 我们只想使用函数进行编程。 (毕竟是“function性编程”)。
  2. 那么,我们有第一个大问题。 这是一个程序:

    f(x) = 2 * x

    g(x,y) = x / y

    我们怎么能说什么是先执行 ? 我们如何使用不超过函数来形成一个有序的函数序列(即程序 )?

    解决scheme: 编写function 。 如果你想先g然后f ,只需写f(g(x,y)) 。 好的但是 …

  3. 更多的问题:一些函数可能会失败 (即g(2,0) ,除以0)。 FP中没有“例外” 。 我们如何解决它?

    解答:让我们让函数返回两种东西 :代替g : Real,Real -> Real (从两个实数的函数变成一个实数),让我们让g : Real,Real -> Real | Nothing g : Real,Real -> Real | Nothing (从两个实际function(实际或没有))。

  4. 但函数应该(更简单)只返回一件事情

    解决方法:让我们创build一个新的types的数据被返回,一个“ 拳击types ”封闭也许是一个真正的或什么都不是。 因此,我们可以有g : Real,Real -> Maybe Real 。 好的但是 …

  5. 现在发生什么f(g(x,y))f还没有准备好消耗一个Maybe Real 。 而且,我们不想改变我们可以用g连接的每一个函数来消耗一个Maybe Real

    解决scheme:让我们有一个特殊的function来“连接”/“撰写”/“链接”function 。 这样,我们可以在幕后调整一个函数的输出来input下面的函数。

    在我们的例子中: g >>= f (连接/合成gf )。 我们希望>>=得到g的输出,检查它,如果不是,就不要调用f并返回Nothing ; 或者相反,提取盒装Real和饲料f 。 (这个algorithm只是>>=Maybetypes的实现)。

  6. 许多其他的问题都可以通过使用相同的模式来解决:1.使用“盒子”来编码/存储不同的含义/值,并且具有像g这样的函数来返回这些“盒装值”。 2.让composer php/连接者g >>= f帮助把g的输出连接到f的input,所以我们根本不需要改变f

  7. 使用这种技术可以解决的显着问题是:

    • 有一个全局状态,函数序列中的每个函数(“程序”)可以共享:solution StateMonad

    • 我们不喜欢“不纯的function”:对同一input产生不同输出的function。 因此,让我们标记这些函数,使它们返回一个标记/盒装值: IO monad。

总的幸福!

就OOP程序员而言(没有任何函数式编程背景),什么是monad?

它解决了什么问题,它使用的最常见的地方是它使用的最常见的地方?

就OO编程而言,monad是一个接口(或者更可能是一个mixin),通过一个types进行参数化,使用两种方法returnbind

  • 如何注入一个值来获得该注入值types的monadic值;
  • 如何使用一个函数,从一个monadic值的非一元值。

它所解决的问题与你期望从任何接口出现的问题types相同,即“我有许多不同的类能够完成不同的事情,但似乎以一种基本的相似性的方式来做这些不同的事情。我可以描述它们之间的相似性吗?即使这些类本身并不比“对象”类本身更接近真正的子types吗?

更具体地说, Monad “接口”类似于IEnumeratorIIterator ,因为它需要一个types,它自己需要一个types。 Monad的主要“观点”是能够将基于内部types的操作连接起来,甚至可以保持或甚至增强主类的信息结构。

我会说最接近OO的类比monad是“ 命令模式 ”。

在命令模式中,您将命令对象中的普通语句或expression式包装起来。 命令对象暴露了执行包装语句的execute方法。 所以语句变成可以传递并随意执行的第一类对象。 可以编写命令,以便通过链接和嵌套命令对象来创build程序对象。

这些命令由一个单独的对象执行,即调用者 。 使用命令模式(而不是执行一系列普通语句)的好处是不同的调用者可以应用不同的逻辑来执行命令。

命令模式可用于添加(或删除)主机语言不支持的语言function。 例如,假设没有例外的OO语言,可以通过向命令公开“try”和“throw”方法来添加exception语义。 当一个命令调用throw时,调用者通过命令的列表(或树)回溯到最后的“尝试”调用。 相反,通过捕获每个单独命令抛出的所有exception,并将它们转换为错误代码,然后传递给下一个命令,可以从语言中移除exception语义(如果您认为exception是错误的 )。

甚至更奇特的执行语义,如事务,非确定性执行或继续可以像这样以一种本身不支持的语言来实现。 如果你想一想,这是一个非常强大的模式。

现在实际上命令模式不被用作这样的通用语言function。 将每个语句转换成单独的类的开销会导致难以忍受的样板代码量。 但原则上它可以用来解决monad用于解决fp中的相同问题。

Christopher League (2010年7月12日)最近发表了“ Monadologie – 焦虑症的专业帮助 ”,这对于继续和monad的话题是相当有趣的。
这个(slideshare)演示文稿的video实际上是 vimeo上。
Monad部分从这张一小时的video开始约37分钟,并从58张幻灯片的幻灯片42开始。

它被提出为“function性编程的领先devise模式”,但实例中使用的语言是Scala,它既是面向对象又是function性的。
您可以从Debasish Ghosh (2008年3月27日)的博客文章“ Monads – 在Scala中抽象计算的另一种方式 ”中了解 Monad在Scala中的更多信息。

如果一个types构造函数 M支持这些操作,则它是monad:

 # the return function def unit[A] (x: A): M[A] # called "bind" in Haskell def flatMap[A,B] (m: M[A]) (f: A => M[B]): M[B] # Other two can be written in term of the first two: def map[A,B] (m: M[A]) (f: A => B): M[B] = flatMap(m){ x => unit(f(x)) } def andThen[A,B] (ma: M[A]) (mb: M[B]): M[B] = flatMap(ma){ x => mb } 

例如(在斯卡拉):

  • Option是一个monad
     def单位[A](x:A):选项[A] =一些(x)

     def flatMap [A​​,B](m:选项[A])(f:A =>选项[B]):选项[B] =
      米匹{
       情况无=>无
        case Some(x)=> f(x)
       }
  • List是Monad
     def unit [A](x:A):List [A] = List(x)

     def flatMap [A​​,B](m:List [A])(f:A => List [B]):List [B] =
      米匹{
        情况无=>无
         case x :: xs => f(x)::: flatMap(xs)(f)
       }

Monad是Scala中的一个大问题,因为构build方便的语法来利用Monad结构:

在Scala中理解

 for { i <- 1 to 4 j <- 1 to i k <- 1 to j } yield i*j*k 

由编译器翻译为:

 (1 to 4).flatMap { i => (1 to i).flatMap { j => (1 to j).map { k => i*j*k }}} 

关键抽象是flatMap ,通过链接绑定计算。
每个flatMap调用都返回相同的数据结构types(但是不同的值),作为链中下一个命令的input。

在上面的代码片段中,flatMap将input一个闭包(SomeType) => List[AnotherType]并返回一个List[AnotherType] 。 重要的一点是,所有flatMaps都采用与input相同的闭包types,并返回与输出相同的types。

这就是“绑定”计算线程的内容 – 在for-comprehension中的每个项目都必须遵守这个相同的types约束。


如果您执行两个操作(可能失败)并将结果传递给第三个操作,如:

 lookupVenue: String => Option[Venue] getLoggedInUser: SessionID => Option[User] reserveTable: (Venue, User) => Option[ConfNo] 

但没有利用Monad,你会得到令人费解的OOP代码,如:

 val user = getLoggedInUser(session) val confirm = if(!user.isDefined) None else lookupVenue(name) match { case None => None case Some(venue) => val confno = reserveTable(venue, user.get) if(confno.isDefined) mailTo(confno.get, user.get) confno } 

而Monad则可以像所有的操作一样使用实际的types( VenueUser ),并且隐藏Option选项,所有这些都是因为语法的平面图:

 val confirm = for { venue <- lookupVenue(name) user <- getLoggedInUser(session) confno <- reserveTable(venue, user) } yield { mailTo(confno, user) confno } 

yield部分只有在三个函数都具有Some[X]才会被执行。 任何None将直接返回confirm


所以:

Monads允许在Functional Programing中进行有序的计算,这使我们能够以一个很好的结构化的forms对动作进行sorting,有点像DSL。

最大的威力来自于将具有不同目的的单子组合成一个应用程序中的可扩展的抽象。

这个由monad执行的动作的sorting和线程化是由语言编译器完成的,它通过闭包的魔力进行转换。


顺便说一下,Monad不仅是FP中使用的计算模型:请参阅此博客文章 。

分类理论提出了许多计算模型。 其中

  • 计算的箭头模型
  • Monad计算模型
  • 计算模型的应用

我写了一篇简短的文章,比较标准的OOP python代码和monadic python代码,用图表演示了底层的计算过程。 它假定以前没有FP的知识。 希望你觉得它有用 – http://nikolaygrozev.wordpress.com/2013/12/10/monads-in-15-minutes/

为了尊重读者,我首先从精确的定义开始,继续以更简单的“简单英语”的解释,然后举例说明。

这里有一个简短而精确的定义,稍微改了一下 :

单子 (在计算机科学中)正式地是一张地图:

  • 将某种给定编程语言的每个typesX发送到一个新的typesT(X) (称为“ T具有X值的计算types”);

  • g:Y->T(Z)两个函数组成的规则f:X->T(Y)g:Y->T(Z) g∘f:X->T(Z)

  • 在一个被称为pure_X:X->T(X)的给定的单位函数pure_X:X->T(X) ,在一个明显的意义上和一个单一的关联方式中,被认为是简单地返回该值的纯计算的值。

所以简单地说, monad从任何typesX传递到另一个typesT(X)规则 ,并且要从两个函数f:X->T(Y)g:Y->T(Z) (你想编写,但不能)到一个新的函数h:X->T(Z) 。 然而, 这不是严格的math意义上的构成。 我们基本上是“弯曲”function的组成或重新定义function是如何组成的。

另外,我们要求monad的组合规则来满足“显而易见的”math公理:

  • 关联性 :用g f ,然后用h (从外部)构成f应该与用h构成g然后用f (从内部)构成相同。
  • 独立财产 :用双方的身份函数来编写f应该产生f

再次,简单地说,我们不能只是疯狂地重新定义我们的函数组成:

  • 我们首先需要关联性能够组成连续的几个函数,例如f(g(h(k(x))) ,而不用担心指定顺序组合函数对,因为单子规则只规定了如何构成一对函数 ,没有公理,我们需要知道哪一对是先组成的(注意不同于交换性性质, f组成的gf组成的g相同,这是不需要的)。
  • 其次,我们需要单位财产,这简单地说,身份构成了我们期望他们的方式。 所以,只要这些身份可以被提取,我们就可以安全地重构函数。

所以再简单地说:单子是满足两个公理的联系性和整体性的扩展和组合函数的规则。

实际上,您希望monad能够通过语言,编译器或框架来为您实现,这些语言,编译器或框架可以为您构build函数。 所以你可以专注于编写你的函数的逻辑,而不用担心如何执行它们的执行。

简而言之,这基本上就是这样。


作为专业的math家,我宁愿避免把fg的“组成”称为h 。 因为在math上,它不是。 把它称为“构图”不正确地假设, h是真正的math构图,事实并非如此。 它甚至不是由fg唯一确定的。 相反,这是我们monad新的“编写规则”function的结果。 即使后者存在,它也可能与实际的math构成完全不同!


Monad 不是一个函子 ! 一个函子F是一个规则,从typesX到typesF(X)并在typesXY之间起作用(态射)到F(X)F(Y)之间的函数(将对象和它的态射发送到态射到类别理论)。 相反,monad发送一函数fg到一个新的h


为了减less干扰,让我试着用小例子来说明这一点,所以你可以直接跳到这一点。

抛出Monad例外的exception

假设我们要编写两个函数:

 f: x -> 1 / x g: y -> 2 * y 

f(0)没有定义,所以抛出exceptione 。 那么如何定义组合值g(f(0))呢? 当然再抛出exception! 也许是一样的。 也许一个新的更新exceptione1

这里究竟发生了什么? 首先,我们需要新的例外价值(不同或相同)。 你可以把它们叫做nothing ,或者什么nothing是,但是其本质是一样的 – 它们应该是新的价值,例如在这里我们的例子中不应该是一个number 。 我宁愿不要将它们调用为null以避免与任何特定语言中的null如何实现混淆。 同样,我更喜欢避免nothing因为它经常与null关联,原则上这是null应该做的,然而,这个原则常常因为任何实际的原因而被弯曲。

什么是exception呢?

对于任何有经验的程序员来说,这是一个微不足道的问题,但是我只想简单地说一句话来消除任何混乱的蠕虫:

exception是封装有关如何执行的无效结果发生的信息的对象。

这可以从抛弃任何细节和返回单个全局值(如NaNnull )或生成长日志列表或发生的事情,将其发送到数据库并在整个分布式数据存储层复制;

这两个极端例外的重要区别在于,第一种情况是没有副作用 。 在第二个有。 这就给我们带来了(千美元)的问题:

在纯函数中是否允许使用exception?

较短的回答 :是的,但只有当他们不导致副作用。

较长的答案。 纯粹的,你的函数的输出必须由它的input唯一确定。 所以我们通过向我们称为exception的新的抽象值e发送0来修改函数f 。 我们确保价值e包含由我们的input唯一确定的外部信息,即x 。 所以这里是一个没有副作用的例子:

 e = { type: error, message: 'I got error trying to divide 1 by 0' } 

这里有一个副作用:

 e = { type: error, message: 'Our committee to decide what is 1/0 is currently away' } 

实际上,如果这个信息在未来可能会改变的话,它只会有副作用。 但是,如果保证永远不会改变,那么价值就变得独一无二,所以没有任何副作用。

为了使它更傻。 返回42的函数显然是纯粹的。 但是如果有人疯狂地决定让一个variables的价值可能改变,那么在新的条件下,同样的function就不再是纯粹的。

请注意,为了简单起见,我使用对象文字符号来表示本质。 不幸的是,在JavaScript这样的语言中,事情被搞砸了,其中的error不是一种类似于我们想要的函数组合的方式,而像nullNaN这样的实际types并不是这样的行为,而是通过一些人为的并不总是直观的types转换。

types扩展

由于我们想要改变exception中的消息,所以我们真的为整个exception对象声明了一个新的typesE ,然后这就是maybe number所做的事情,除了它令人困惑的名字外,它的types是number或是新的exceptiontypesE ,所以它确实是联合number | E number | E numberE 。 特别是,这取决于我们如何构buildE ,这既不是build议也不反映在名字, maybe number

什么是function组成?

它是以函数f: X -> Yg: Y -> Z并构造它们的组成为函数h: X -> Z满足h(x) = g(f(x))的math运算。 当f(x)不允许作为g参数时,就会出现这个定义的问题。

在math中,这些function不能没有额外的工作组成。 我们上面的fg例子的严格的math解决scheme是从f的定义集合中去掉0 。 有了这个新的定义(新的x更具限制性的types), f就变成了可组合的g

但是,在编程中限制f的定义是不实际的。 相反,可以使用exception。

或者作为另一种方法,人为值被创build为NaNundefinednullInfinity等。因此,您将1/0评估为Infinity ,将1/0评估为-Infinity 。 然后强制新值返回到您的expression式,而不是抛出exception。 导致结果你可能会或可能不会发现可预见的:

 1/0 // => Infinity parseInt(Infinity) // => NaN NaN < 0 // => false false + 1 // => 1 

我们又回到了正常的数字,准备继续前进;)

JavaScript allows us to keep executing numerical expressions at any costs without throwing errors as in the above example. That means, it also allows to compose functions. Which is exactly what monad is about – it is a rule to compose functions satisfying the axioms as defined at the beginning of this answer.

But is the rule of composing function, arising from JavaScript's implementation for dealing with numerical errors, a monad?

To answer this question, all you need is to check the axioms (left as exercise as not part of the question here;).

Can throwing exception be used to construct a monad?

Indeed, a more useful monad would instead be the rule prescribing that if f throws exception for some x , so does its composition with any g . Plus make the exception E globally unique with only one possible value ever ( terminal object in category theory). Now the two axioms are instantly checkable and we get a very useful monad. And the result is what is well-known as the maybe monad .

A monad is a data type that encapsulates a value and to which essentially two operations can be applied:

  • return x creates a value of the monad type that encapsulates x
  • m >>= f (read it as "the bind operator") applies the function f to the value in the monad m

That's what a monad is. There are a few more technicalities , but basically those two operations define a monad. The real question is what a monad does , and that depends on the monad — lists are monads, Maybes are monads, IO operations are monads. All that it means when we say those things are monads is that they have the monad interface of return and >>= .

From wikipedia :

In functional programming, a monad is a kind of abstract data type used to represent computations (instead of data in the domain model). Monads allow the programmer to chain actions together to build a pipeline, in which each action is decorated with additional processing rules provided by the monad. Programs written in functional style can make use of monads to structure procedures that include sequenced operations, 1 [2] or to define arbitrary control flows (like handling concurrency, continuations, or exceptions).

Formally, a monad is constructed by defining two operations (bind and return) and a type constructor M that must fulfill several properties to allow the correct composition of monadic functions (ie functions that use values from the monad as their arguments). The return operation takes a value from a plain type and puts it into a monadic container of type M. The bind operation performs the reverse process, extracting the original value from the container and passing it to the associated next function in the pipeline.

A programmer will compose monadic functions to define a data-processing pipeline. The monad acts as a framework, as it's a reusable behavior that decides the order in which the specific monadic functions in the pipeline are called, and manages all the undercover work required by the computation.[3] The bind and return operators interleaved in the pipeline will be executed after each monadic function returns control, and will take care of the particular aspects handled by the monad.

I believe it explains it very well.

I'll try to make the shortest definition I can manage using OOP terms:

A generic class CMonadic<T> is a monad if it defines at least the following methods:

 class CMonadic<T> { static CMonadic<T> create(T t); // aka, "return" in Haskell public CMonadic<U> flatMap<U>(Func<T, CMonadic<U>> f); // aka "bind" in Haskell } 

and if the following laws apply for all types T and their possible values t

left identity:

 CMonadic<T>.create(t).flatMap(f) == f(t) 

right identity

 instance.flatMap(CMonadic<T>.create) == instance 

associativity:

 instance.flatMap(f).flatMap(g) == instance.flatMap(t => f(t).flatMap(g)) 

例如

A List monad may have:

 List<int>.create(1) --> [1] 

And flatMap on the list [1,2,3] could work like so:

 intList.flatMap(x => List<int>.makeFromTwoItems(x, x*10)) --> [1,10,2,20,3,30] 

Iterables and Observables can also be made monadic, as well as Promises and Tasks.

Commentary :

Monads are not that complicated. The flatMap function is a lot like the more commonly encountered map . It receives a function argument (also known as delegate), which it may call (immediately or later, zero or more times) with a value coming from the generic class. It expects that passed function to also wrap its return value in the same kind of generic class. To help with that, it provides create , a constructor that can create an instance of that generic class from a value. The return result of flatMap is also a generic class of the same type, often packing the same values that were contained in the return results of one or more applications of flatMap to the previously contained values. This allows you to chain flatMap as much as you want:

 intList.flatMap(x => List<int>.makeFromTwo(x, x*10)) .flatMap(x => x % 3 == 0 ? List<string>.create("x = " + x.toString()) : List<string>.empty()) 

It just so happens that this kind of generic class is useful as a base model for a huge number of things. This (together with the category theory jargonisms) is the reason why Monads seem so hard to understand or explain. They're a very abstract thing and only become obviously useful once they're specialized.

For example, you can model exceptions using monadic containers. Each container will either contain the result of the operation or the error that has occured. The next function (delegate) in the chain of flatMap callbacks will only be called if the previous one packed a value in the container. Otherwise if an error was packed, the error will continue to propagate through the chained containers until a container is found that has an error handler function attached via a method called .orElse() (such a method would be an allowed extension)

Notes : Functional languages allow you to write functions that can operate on any kind of a monadic generic class. For this to work, one would have to write a generic interface for monads. I don't know if its possible to write such an interface in C#, but as far as I know it isn't:

 interface IMonad<T> { static IMonad<T> create(T t); // not allowed public IMonad<U> flatMap<U>(Func<T, IMonad<U>> f); // not specific enough, // because the function must return the same kind of monad, not just any monad } 

Whether a monad has a "natural" interpretation in OO depends on the monad. In a language like Java, you can translate the maybe monad to the language of checking for null pointers, so that computations that fail (ie, produce Nothing in Haskell) emit null pointers as results. You can translate the state monad into the language generated by creating a mutable variable and methods to change its state.

A monad is a monoid in the category of endofunctors.

The information that sentence puts together is very deep. And you work in a monad with any imperative language. A monad is a "sequenced" domain specific language. It satisfies certain interesting properties, which taken together make a monad a mathematical model of "imperative programming". Haskell makes it easy to define small (or large) imperative languages, which can be combined in a variety of ways.

As an OO programmer, you use your language's class hierarchy to organize the kinds of functions or procedures that can be called in a context, what you call an object. A monad is also an abstraction on this idea, insofar as different monads can be combined in arbitrary ways, effectively "importing" all of the sub-monad's methods into the scope.

Architecturally, one then uses type signatures to explicitly express which contexts may be used for computing a value.

One can use monad transformers for this purpose, and there is a high quality collection of all of the "standard" monads:

  • Lists (non-deterministic computations, by treating a list as a domain)
  • Maybe (computations that can fail, but for which reporting is unimportant)
  • Error (computations that can fail and require exception handling
  • Reader (computations that can be represented by compositions of plain Haskell functions)
  • Writer (computations with sequential "rendering"/"logging" (to strings, html etc)
  • Cont (continuations)
  • IO (computations that depend on the underlying computer system)
  • State (computations whose context contains a modifiable value)

with corresponding monad transformers and type classes. Type classes allow a complementary approach to combining monads by unifying their interfaces, so that concrete monads can implement a standard interface for the monad "kind". For example, the module Control.Monad.State contains a class MonadState sm, and (State s) is an instance of the form

 instance MonadState s (State s) where put = ... get = ... 

The long story is that a monad is a functor which attaches "context" to a value, which has a way to inject a value into the monad, and which has a way to evaluate values with respect to the context attached to it, at least in a restricted way.

所以:

 return :: a -> ma 

is a function which injects a value of type a into a monad "action" of type m a.

 (>>=) :: ma -> (a -> mb) -> mb 

is a function which takes a monad action, evaluates its result, and applies a function to the result. The neat thing about (>>=) is that the result is in the same monad. In other words, in m >>= f, (>>=) pulls the result out of m, and binds it to f, so that the result is in the monad. (Alternatively, we can say that (>>=) pulls f into m and applies it to the result.) As a consequence, if we have f :: a -> mb, and g :: b -> mc, we can "sequence" actions:

 m >>= f >>= g 

Or, using "do notation"

 do x <- m y <- fx gy 

The type for (>>) might be illuminating. 它是

 (>>) :: ma -> mb -> mb 

It corresponds to the (;) operator in procedural languages like C. It allows do notation like:

 m = do x <- someQuery someAction x theNextAction andSoOn 

In mathematical and philosopical logic, we have frames and models, which are "naturally" modelled with monadism. An interpretation is a function which looks into the model's domain and computes the truth value (or generalizations) of a proposition (or formula, under generalizations). In a modal logic for necessity, we might say that a proposition is necessary if it is true in "every possible world" — if it is true with respect to every admissible domain. This means that a model in a language for a proposition can be reified as a model whose domain consists of collection of distinct models (one corresponding to each possible world). Every monad has a method named "join" which flattens layers, which implies that every monad action whose result is a monad action can be embedded in the monad.

 join :: m (ma) -> ma 

More importantly, it means that the monad is closed under the "layer stacking" operation. This is how monad transformers work: they combine monads by providing "join-like" methods for types like

 newtype MaybeT ma = MaybeT { runMaybeT :: m (Maybe a) } 

so that we can transform an action in (MaybeT m) into an action in m, effectively collapsing layers. In this case, runMaybeT :: MaybeT ma -> m (Maybe a) is our join-like method. (MaybeT m) is a monad, and MaybeT :: m (Maybe a) -> MaybeT ma is effectively a constructor for a new type of monad action in m.

A free monad for a functor is the monad generated by stacking f, with the implication that every sequence of constructors for f is an element of the free monad (or, more exactly, something with the same shape as the tree of sequences of constructors for f). Free monads are a useful technique for constructing flexible monads with a minimal amount of boiler-plate. In a Haskell program, I might use free monads to define simple monads for "high level system programming" to help maintain type safety (I'm just using types and their declarations. Implementations are straight-forward with the use of combinators):

 data RandomF ra = GetRandom (r -> a) deriving Functor type Random ra = Free (RandomF r) a type RandomT ma = Random (ma) (ma) -- model randomness in a monad by computing random monad elements. getRandom :: Random rr runRandomIO :: Random ra -> IO a (use some kind of IO-based backend to run) runRandomIO' :: Random ra -> IO a (use some other kind of IO-based backend) runRandomList :: Random ra -> [a] (some kind of list-based backend (for pseudo-randoms)) 

Monadism is the underlying architecture for what you might call the "interpreter" or "command" pattern, abstracted to its clearest form, since every monadic computation must be "run", at least trivially. (The runtime system runs the IO monad for us, and is the entry point to any Haskell program. IO "drives" the rest of the computations, by running IO actions in order).

The type for join is also where we get the statement that a monad is a monoid in the category of endofunctors. Join is typically more important for theoretical purposes, in virtue of its type. But understanding the type means understanding monads. Join and monad transformer's join-like types are effectively compositions of endofunctors, in the sense of function composition. To put it in a Haskell-like pseudo-language,

Foo :: m (ma) <-> (m . m) a

A monad is an array of functions

(Pst: an array of functions is just a computation).

Actually, instead of a true array (one function in one cell array) you have those functions chained by another function >>=. The >>= allows to adapt the results from function i to feed function i+1, perform calculations between them or, even, not to call function i+1.

The types used here are "types with context". This is, a value with a "tag". The functions being chained must take a "naked value" and return a tagged result. One of the duties of >>= is to extract a naked value out of its context. There is also the function "return", that takes a naked value and puts it with a tag.

An example with Maybe . Let's use it to store a simple integer on which make calculations.

 -- a * b multiply :: Int -> Int -> Maybe Int multiply ab = return (a*b) -- divideBy 5 100 = 100 / 5 divideBy :: Int -> Int -> Maybe Int divideBy 0 _ = Nothing -- dividing by 0 gives NOTHING divideBy denom num = return (quot num denom) -- quotient of num / denom -- tagged value val1 = Just 160 -- array of functions feeded with val1 array1 = val1 >>= divideBy 2 >>= multiply 3 >>= divideBy 4 >>= multiply 3 -- array of funcionts created with the do notation -- equals array1 but for the feeded val1 array2 :: Int -> Maybe Int array2 n = do v <- divideBy 2 n v <- multiply 3 v v <- divideBy 4 v v <- multiply 3 v return v -- array of functions, -- the first >>= performs 160 / 0, returning Nothing -- the second >>= has to perform Nothing >>= multiply 3 .... -- and simply returns Nothing without calling multiply 3 .... array3 = val1 >>= divideBy 0 >>= multiply 3 >>= divideBy 4 >>= multiply 3 main = do print array1 print (array2 160) print array3 

Just to show that monads are array of functions with helper operations, consider the equivalent to the above example, just using a real array of functions

 type MyMonad = [Int -> Maybe Int] -- my monad as a real array of functions myArray1 = [divideBy 2, multiply 3, divideBy 4, multiply 3] -- function for the machinery of executing each function i with the result provided by function i-1 runMyMonad :: Maybe Int -> MyMonad -> Maybe Int runMyMonad val [] = val runMyMonad Nothing _ = Nothing runMyMonad (Just val) (f:fs) = runMyMonad (f val) fs 

And it would be used like this:

 print (runMyMonad (Just 160) myArray1) 

Monads in typical usage are the functional equivalent of procedural programming's exception handling mechanisms.

In modern procedural languages, you put an exception handler around a sequence of statements, any of which may throw an exception. If any of the statements throws an exception, normal execution of the sequence of statements halts and transfers to an exception handler.

Functional programming languages, however, philosophically avoid exception handling features due to the "goto" like nature of them. The functional programming perspective is that functions should not have "side-effects" like exceptions that disrupt program flow.

In reality, side-effects cannot be ruled out in the real world due primarily to I/O. Monads in functional programming are used to handle this by taking a set of chained function calls (any of which might produce an unexpected result) and turning any unexpected result into encapsulated data that can still flow safely through the remaining function calls.

The flow of control is preserved but the unexpected event is safely encapsulated and handled.

If you've ever used Powershell, the patterns Eric described should sound familiar. Powershell cmdlets are monads; functional composition is represented by a pipeline .

Jeffrey Snover's interview with Erik Meijer goes into more detail.

From a practical point of view (summarizing what has been said in many previous answers and related articles), it seems to me that one of the fundamental "purposes" (or usefulness) of the monad is to leverage the dependencies implicit in recursive method invocations aka function composition (ie when f1 calls f2 calls f3, f3 needs to be evaluated before f2 before f1) to represent sequential composition in a natural way, especially in the context of a lazy evaluation model (that is, sequential composition as a plain sequence, eg "f3(); f2(); f1();" in C – the trick is especially obvious if you think of a case where f3, f2 and f1 actually return nothing [their chaining as f1(f2(f3)) is artificial, purely intended to create sequence]).

This is especially relevant when side-effects are involved, ie when some state is altered (if f1, f2, f3 had no side-effects, it wouldn't matter in what order they're evaluated; which is a great property of pure functional languages, to be able to parallelize those computations for example). The more pure functions, the better.

I think from that narrow point of view, monads could be seen as syntactic sugar for languages that favor lazy evaluation (that evaluate things only when absolutely necessary, following an order that does not rely on the presentation of the code), and that have no other means of representing sequential composition. The net result is that sections of code that are "impure" (ie that do have side-effects) can be presented naturally, in an imperative manner, yet are cleanly separated from pure functions (with no side-effects), which can be evaluated lazily.

This is only one aspect though, as warned here .

See my answer to "What is a monad?"

It begins with a motivating example, works through the example, derives an example of a monad, and formally defines "monad".

It assumes no knowledge of functional programming and it uses pseudocode with function(argument) := expression syntax with the simplest possible expressions.

This C++ program is an implementation of the pseudocode monad. (For reference: M is the type constructor, feed is the "bind" operation, and wrap is the "return" operation.)

 #include <iostream> #include <string> template <class A> class M { public: A val; std::string messages; }; template <class A, class B> M<B> feed(M<B> (*f)(A), M<A> x) { M<B> m = f(x.val); m.messages = x.messages + m.messages; return m; } template <class A> M<A> wrap(A x) { M<A> m; m.val = x; m.messages = ""; return m; } class T {}; class U {}; class V {}; M<U> g(V x) { M<U> m; m.messages = "called g.\n"; return m; } M<T> f(U x) { M<T> m; m.messages = "called f.\n"; return m; } int main() { V x; M<T> m = feed(f, feed(g, wrap(x))); std::cout << m.messages; } 

In OO terms, a monad is a fluent container.

The minimum requirement is a definition of class <A> Something that supports a constructor Something(A a) and at least one method Something<B> flatMap(Function<A, Something<B>>)

Arguably, it also counts if your monad class has any methods with signature Something<B> work() which preserves the class's rules — the compiler bakes in flatMap at compile time.

Why is a monad useful? Because it is a container that allows chain-able operations that preserve semantics. For example, Optional<?> preserves the semantics of isPresent for Optional<String> , Optional<Integer> , Optional<MyClass> , etc.

As a rough example,

 Something<Integer> i = new Something("a") .flatMap(doOneThing) .flatMap(doAnother) .flatMap(toInt) 

Note we start with a string and end with an integer. Pretty cool.

In OO, it might take a little hand-waving, but any method on Something that returns another subclass of Something meets the criterion of a container function that returns a container of the original type.

That's how you preserve semantics — ie the container's meaning and operations don't change, they just wrap and enhance the object inside the container.