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

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


当前回答

在了解这些信息时,对我帮助最大的两件事是:

第8章,“函数解析器”,摘自Graham Hutton的《Haskell编程》一书。实际上,这根本没有提到monad,但如果您能够通读第章并真正理解其中的所有内容,特别是如何评估一系列绑定操作,您将了解monad的内部结构。预计这需要多次尝试。

关于修道院的教程。这提供了几个很好的例子来说明它们的用途,我不得不说,我在Appendex中的类比是为我工作的。

其他回答

如果你要求对如此抽象的东西做出简洁、实用的解释,那么你只能希望得到一个抽象的答案:

a -> b

是表示从as到bs的计算的一种方式。您可以将计算链接起来,也可以将它们组合在一起:

(b -> c) -> (a -> b) -> (a -> c)

更复杂的计算需要更复杂的类型,例如:

a -> f b

是从as到bs到fs的计算类型。您还可以编写它们:

(b -> f c) -> (a -> f b) -> (a -> f c)

事实证明,这种模式无处不在,并且与上面的第一个组合具有相同的财产(结合性、右-和左-同一性)。

人们必须给这个模式起一个名字,但如果知道第一个组合被正式描述为半群体,这会有帮助吗?

“单子和圆括号一样有趣和重要”(奥列格·基斯廖夫)

对于来自命令式背景(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似乎就像解释控制流语句一样。想象一下,一个非程序员要求你解释它们?

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

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

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

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

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

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

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

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

在Scala的上下文中,您会发现以下是最简单的定义。基本上,flatMap(或bind)是“关联”的,并且存在一个标识。

trait M[+A] {
  def flatMap[B](f: A => M[B]): M[B] // AKA bind

  // Pseudo Meta Code
  def isValidMonad: Boolean = {
    // for every parameter the following holds
    def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
      x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))

    // for every parameter X and x, there exists an id
    // such that the following holds
    def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
      x.flatMap(id) == x
  }
}

E.g.

// These could be any functions
val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
val g: String => Option[Double] = string => Some(3.14)

// Observe these are identical. Since Option is a Monad 
// they will always be identical no matter what the functions are
scala> Some(7).flatMap(f).flatMap(g)
res211: Option[Double] = Some(3.14)

scala> Some(7).flatMap(f(_).flatMap(g))
res212: Option[Double] = Some(3.14)


// As Option is a Monad, there exists an identity:
val id: Int => Option[Int] = x => Some(x)

// Observe these are identical
scala> Some(7).flatMap(id)
res213: Option[Int] = Some(7)

scala> Some(7)
res214: Some[Int] = Some(7)

注:严格地说,函数编程中的Monad的定义与范畴理论中的Monard的定义不同,后者是按映射和展平的顺序定义的。尽管它们在某些映射下是等价的。这个演示非常好:http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category