什么是monad?
最近简单地看了一下Haskell,对于monad实际上是什么来说,简单,简洁,实用的解释是什么?
我发现我所遇到的大部分解释是相当无法访问的,缺乏实际的细节。
第一:如果你不是math家, monad这个词有点空虚。 另一个术语是计算生成器 ,它对于它们实际上有用的东西有一些描述。
你要求实际的例子:
例1:列表理解 :
[x*2 | x<-[1..10], odd x]
这个expression式返回从1到10范围内的所有奇数的双精度。非常有用!
事实certificate,这对于List monad中的一些操作实际上只是语法糖。 同样的列表理解可以写成:
do x <- [1..10] if odd x then [x * 2] else []
甚至:
[1..10] >>= (\x -> if odd x then [x*2] else [])
示例2:input/输出 :
do putStrLn "What is your name?" name <- getLine putStrLn ("Welcome, " ++ name ++ "!")
这两个例子都使用monads,AKA计算构build器。 共同的主题是单子链操作以某种特定的,有用的方式。 在列表理解中,操作是链接的,如果操作返回一个列表,则对列表中的每个项目执行以下操作。 另一方面,IO monad依次执行操作,但传递一个“隐藏variables”,代表“世界的状态”,这允许我们以纯function的方式编写I / O代码。
事实certificate, 链式操作的模式是非常有用的,在Haskell中用于很多不同的事情。
另一个例子是exception:使用Error
monad,操作被链接在一起,以便它们按顺序执行,除非抛出错误,在这种情况下,链的其余部分被放弃。
list-comprehension语法和do-notation都是使用>>=
操作符链接操作的语法糖。 monad基本上只是一个支持>>=
运算符的types。
例3:parsing器
这是一个非常简单的parsing器,可以parsing带引号的string或数字:
parseExpr = parseString <|> parseNumber parseString = do char '"' x <- many (noneOf "\"") char '"' return (StringValue x) parseNumber = do num <- many1 digit return (NumberValue (read num))
操作char
, digit
等非常简单。 他们要么匹配,要么不匹配。 神奇的是pipe理控制stream程的monad:操作顺序执行,直到匹配失败,在这种情况下,monad回溯到最新的<|>
并尝试下一个选项。 再一次,用一些额外的,有用的语义链接操作的方式。
例4:asynchronous编程
上面的例子在Haskell中,但是F#也支持monad。 这个例子是从唐Syme偷来的:
let AsyncHttp(url:string) = async { let req = WebRequest.Create(url) let! rsp = req.GetResponseAsync() use stream = rsp.GetResponseStream() use reader = new System.IO.StreamReader(stream) return reader.ReadToEnd() }
这个方法提取一个网页。 冲突线是使用GetResponseAsync
– 它实际上等待在一个单独的线程的响应,而主线程从函数返回。 当收到响应时,最后三行在产生的线程上执行。
在大多数其他语言中,您将不得不为处理响应的行显式创build一个单独的函数。 async
单元能够自行“分割”块并推迟后半部分的执行。 ( async {}
语法指示块中的控制stream由async
单元定义。)
他们是如何工作的
那么monad怎么能做这些奇特的控制stream的东西呢? 在do-block(或者在F#中调用一个计算expression式)中实际发生的事情是每一个操作(基本上每一行)被包装在一个单独的匿名函数中。 这些函数然后使用bind
操作符(在Haskell中拼写>>=
)进行组合。 由于bind
操作将函数组合在一起,因此它可以按照合适的方式执行它们:顺序地,多次地,相反地抛弃一些,在感觉像一个单独的线程时执行一些。
作为一个例子,这是来自例2的IO代码的扩展版本:
putStrLn "What is your name?" >>= (\_ -> getLine) >>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))
这是丑陋的,但实际上正在发生的事情也更为明显。 >>=
运算符是神奇的成分:它取一个值(在左边)并将它与一个函数(在右边)结合,产生一个新的值。 这个新的值然后由下一个>>=
操作符来进行,再次与一个函数结合以产生一个新的值。 >>=
可以被看作是一个迷你评估者。
请注意>>=
对于不同的types是重载的,所以每个monad都有自己的>>=
实现。 (链中的所有操作都必须是相同monad的types,否则>>=
操作符将不起作用。)
>>=
的最简单可能的实现只是取左边的值并将其应用到右边的函数并返回结果,但如前所述,使整个模式有用的是什么时候有什么额外的事情发生monad的>>=
的实现。
在从一个操作到另一个操作的值传递方式中还有一些额外的巧妙之处,但这需要对Haskelltypes系统进行更深入的解释。
加起来
在Haskell术语中,monad是一个参数化types,它是Monadtypes类的一个实例,它定义了>>=
以及其他一些运算符。 用外行人的话来说,monad只是一个定义>>=
操作的types。
本身>>=
仅仅是一个繁琐的链接函数的方式,但是由于隐藏了“pipe道”的符号的存在,monadic操作变成了一个非常好的和有用的抽象,在语言中很多地方是有用的,并有助于在语言中创build自己的迷你语言。
monad为什么很难?
对于许多Haskell学习者来说,monad是一个障碍,就像砖墙一样。 这并不是monads本身很复杂,而是实现依赖于许多其他高级Haskellfunction,如参数化types,types类等等。 问题是Haskell I / O是基于monad的,I / O可能是你想学习一门新语言时要理解的第一件事 – 毕竟,创build不产生任何程序输出。 对于这个鸡与鸡蛋的问题,我没有立即的解决办法,除非把I / O当作“魔术发生在这里”,除非你有足够的语言经验。 抱歉。
优秀的单子博客: http : //adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
解释“monad是什么”有点像说“什么是数字”? 我们一直使用数字。 但想象一下,遇到了一个对数字一无所知的人。 你会怎么解释什么是数字? 你怎么会开始描述为什么这可能是有用的?
什么是monad? 简短的回答:这是一个链接操作的具体方式。
实际上,您正在编写执行步骤,并使用“绑定函数”将它们链接在一起。 (在Haskell中,它的名字是>>=
。你可以自己把调用写到绑定操作符,或者你可以使用语法糖来让编译器为你插入这些函数调用。 但是不pipe怎样,每一步都是通过调用这个绑定函数来分开的。
所以绑定函数就像分号一样; 它分离了一个过程中的步骤。 绑定函数的工作是从上一步获取输出,并将其input到下一步。
这听起来不太难,对吧? 但是有不止一种单子。 为什么? 怎么样?
那么,绑定函数就可以从一个步骤中得到结果,并将其馈送到下一步。 但是,如果这是“全部”,monad确实…并不是很有用。 这是很重要的理解:每个有用的 monad 除了作为一个单子之外还有别的东西。 每一个有用的 monad都有一个“特殊的力量”,这使得它独一无二。
(一个没有任何特别之处的monad被称为“身份monad”,而不是身份函数,这听起来像是一个完全没有意义的事情,但事实certificate这不是……但这是另一个故事。
基本上,每个monad都有自己的绑定函数的实现。 而且你可以编写一个绑定函数,以便在执行步骤之间做一些事情。 例如:
-
如果每个步骤都返回成功/失败指示符,则只有在前一个成功的情况下,才能执行下一步的绑定。 这样,一个失败的步骤会“自动”中止整个序列,而无需进行任何有条件的testing。 ( 失败Monad 。)
-
扩展这个想法,你可以实现“例外”。 ( Error Monad或Exception Monad 。)因为你自己定义它们,而不是一个语言特性,所以你可以定义它们是如何工作的。 (例如,也许你想忽略前两个exception,只有当抛出第三个exception时才放弃。)
-
您可以使每个步骤返回多个结果 ,并使绑定函数遍历它们,为每个步骤提供下一步。 通过这种方式,在处理多个结果时,您不必在整个地方继续编写循环。 绑定函数“自动”完成所有的工作。 ( 名单Monad 。)
-
除了将“结果”从一个步骤传递到另一个步骤之外,还可以让bind函数传递额外的数据 。 这些数据现在不会显示在您的源代码中,但您仍然可以从任何地方访问它,而无需手动将其传递给每个function。 (The Reader Monad 。)
-
你可以这样做,以便“额外的数据”可以被replace。 这使您可以模拟破坏性更新 ,而无需进行破坏性更新。 ( 国家Monad和它的表兄作家Monad 。)
-
因为你只是在模拟破坏性的更新,所以你可以轻而易举地做一些真正的破坏性更新是不可能的事情。 例如,您可以撤消上一次更新 ,也可以恢复到旧版本 。
-
你可以让一个monad在计算可以暂停的地方进行,所以你可以暂停你的程序,进入并修改内部状态数据,然后重新开始。
-
你可以实现“continuations”作为monad。 这可以让你打破人心!
所有这些和更多是可能的单子。 当然, 如果没有 monad,这一切也是完全可能的。 使用monads就大大简化了。
但是, 你可以发明Monad!
sigfpe说:
但是所有这些都将monad引入了需要解释的深奥之处。 但是我想争辩的是,他们根本不是深奥的。 事实上,面对函数式编程中的各种问题,你将不可避免地被带到某些解决scheme,所有这些都是单子的例子。 事实上,如果你还没有的话,我希望能让你发明它们。 这是一小步注意到,所有这些解决scheme实际上是相同的解决scheme。 读完这些之后,你可能会更好地理解monad上的其他文档,因为你会认识到你已经发现的一切。
单子试图解决的许多问题都与副作用有关。 所以我们将从他们开始。 (请注意,monads让你做的不仅仅是处理副作用,特别是许多types的容器对象可以被看作monad,monad的一些介绍很难调和monads的这两种不同的用法,只关注一个另一个。)
在诸如C ++之类的命令式编程语言中,函数与math的function没有任何关系。 例如,假设我们有一个C ++函数,它接受一个浮点参数并返回浮点结果。 从表面上看,它可能看起来有点像一个math函数映射reals reals,但是一个C ++函数可以做的不仅仅是返回一个依赖于它的参数的数字。 它可以读取和写入全局variables的值以及将输出写入屏幕并接收来自用户的input。 然而,在一个纯粹的函数式语言中,一个函数只能读取在它的参数中提供给它的东西,它能够影响世界的唯一方法就是通过它返回的值。
实际上,与Monad的共同理解相反,它们与国家无关。 Monad只是一种包装东西的方法,并提供方法来对包装的东西进行操作而不打开包装。
例如,你可以创build一个types来包装另一个,在Haskell中:
data Wrapped a = Wrap a
包装我们定义的东西
return :: a -> Wrapped a return x = Wrap x
为了在不展开的情况下执行操作,比如说你有一个函数f :: a -> b
,那么你可以这样做来提升这个函数对包装值的作用:
fmap :: (a -> b) -> (Wrapped a -> Wrapped b) fmap f (Wrap x) = Wrap (fx)
这是关于所有要了解的。 但是,事实certificate,这个提升有一个更一般的function,这是bind
:
bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b) bind f (Wrap x) = fx
bind
可以做比fmap
多一点,但反之亦然。 实际上, fmap
只能用bind
和return
来定义。 所以,在定义monad时,你给它的types(这里是Wrapped a
),然后说出它的return
和bind
操作是如何工作的。
很酷的是,事实certificate,这是一个普遍的模式,它遍布全球,以纯粹的方式封装国家只是其中之一。
有关如何使用Monad来引入函数依赖关系,从而控制评估顺序(如Haskell的IO monad中使用的顺序)的好文章,请查看IO Inside 。
至于理解单子,不要太担心。 阅读关于他们你觉得有趣的事情,不要担心,如果你不明白。 然后,像Haskell这样的语言跳水是一种方式。 单子是通过实践理解stream入大脑的这些东西之一,有一天,你突然意识到你理解了它们。
monad是一个有两个操作的数据types: >>=
(又名bind
)和return
(又名unit
)。 return
值取一个任意值,并用它创buildmonad的一个实例。 >>=
接受monad的一个实例,并在其上映射一个函数。 (你已经可以看到monad是一种奇怪的数据types,因为在大多数编程语言中,你不能编写一个函数来获取一个任意的值,并从中创build一个types,Monad使用一种参数多态 。
在Haskell中,monad接口被写入
class Monad m where return :: a -> ma (>>=) :: forall ab . ma -> (a -> mb) -> mb
这些操作应该服从某些“规律”,但这并不重要:“规律”只是规定了操作的合理执行方式应该遵循的方式(基本上, >>=
和return
应该如何同意值的变化成monad实例,而>>=
是关联的)。
Monad不仅仅是状态和I / O:它们抽象了一个常见的计算模式,包括处理状态,I / O,exception和非确定性。 可能最简单的monad是理解列表和选项types:
instance Monad [ ] where [] >>= k = [] (x:xs) >>= k = kx ++ (xs >>= k) return x = [x] instance Monad Maybe where Just x >>= k = kx Nothing >>= k = Nothing return x = Just x
其中[]
和:
是列表构造函数, ++
是连接运算符, Just
和Nothing
是Maybe
构造函数。 这两个monad在它们各自的数据types上封装了常用和有用的计算模式(请注意,这些模式与副作用或I / O都没有关系)。
你真的必须玩一些不平凡的Haskell代码来欣赏monad是什么,为什么他们是有用的。
你应该先了解一个函数是什么。 在此之前,了解更高级的function。
高阶函数只是一个将函数作为参数的函数。
一个函子是任何types结构T
,它存在一个高阶函数,称之为map
,它将a- a -> b
(给定任意两个typesa
和b
)的函数转换成函数T a -> T b
。 这个map
函数还必须服从身份和组合的规律,以便以下expression式对所有x
, p
和q
(Haskell符号)返回true:
map id = id map (p . q) = map p . map q
例如,一个名为List
的types构造函数如果配备了一个types为(a -> b) -> List a -> List b
的函数,它就遵从上述定律。 唯一的实际执行是显而易见的。 得到的List a -> List b
函数迭代给定列表,为每个元素调用(a -> b)
函数,并返回结果列表。
monad本质上只是一个函数T
有两个额外的方法, join
, T (T a) -> T a
和a -> T a
typesa -> T a
unit
(有时称为return
, fork
或pure
)。 对于Haskell中的列表:
join :: [[a]] -> [a] pure :: a -> [a]
为什么这很有用? 例如,因为可以使用返回列表的函数map
列表。 Join
将得到列表的结果列表并连接它们。 List
是一个单子,因为这是可能的。
你可以写一个map
的函数,然后join
。 这个函数被称为bind
,或flatMap
,或(>>=)
或(=<<)
。 这通常是在Haskell中如何给出monad实例的。
单子必须满足某些法则,即join
必须是联合的。 这意味着如果你有一个types为[[[a]]]
的值x
,那么join (join x)
应该等于join (map join x)
。 pure
必须是联接的身份,以便join (pure x) == x
。
[免责声明:我仍然试图充分讨好单子。 以下是我迄今为止所了解的内容。 如果这是错误的,希望有知识的人会在地毯上打电话给我。]
Arnar写道:
Monad只是一种包装东西的方法,并提供方法来对包装的东西进行操作而不打开包装。
就是这样。 这个想法是这样的:
-
你采取某种价值,并包含一些额外的信息。 就像这个值是某种types的(例如一个整数或者一个string)一样,所以附加信息是某种types的。
例如,额外的信息可能是一个
Maybe
或IO
。 -
然后你有一些运营商,允许你运行包装的数据,同时携带这些额外的信息。 这些运算符使用附加信息来决定如何更改包装值上的操作行为。
例如,一个
Maybe Int
可以是一个Just Int
或Nothing
。 现在,如果将Maybe Int
添加到Maybe Int
,则运算符将检查它们是否都是Just Int
,如果是,则将展开Int
,将它们传递给添加运算符,重新包装生成的Int
到一个新的Just Int
(这是一个有效的Maybe Int
),从而返回一个Maybe Int
。 但是,如果其中一个是Nothing
,那么这个运算符将立即返回Nothing
,这也是一个有效的Maybe Int
。 这样,你可以假装你的Maybe Int
只是正常的数字,并对它们执行常规的math运算。 如果你得不到Nothing
,你的方程式仍然会产生正确的结果, 而不必在Nothing
地方乱扔垃圾 。
但是这个例子就是Maybe
。 如果额外的信息是一个IO
,那么为IO
定义的特殊运算符将被调用,而在执行加法之前它可以做一些完全不同的事情。 (好吧,一起添加两个IO Int
可能是无意义的 – 我还不确定。)(另外,如果你注意到Maybe
例子,你已经注意到“用额外的东西包装一个值”并不总是正确的。但是难以确切,正确和精确,而不是难以理解。)
基本上, “monad”大致意思是“模式” 。 但是,除了一本充满非正式解释和专门命名的模式的书,你现在有一个语言结构 – 语法和所有 – 它允许你在程序中声明新的模式 。 (这里的不精确是所有的模式必须遵循一个特定的forms,所以一个单子不像一个模式一样通用,但是我认为这是大多数人所了解和理解的最接近的术语。)
这就是为什么人们觉得单子如此混乱:因为它们是一个通用的概念。 要问是什么让一个单子变成了一个模糊的东西,以至于问什么使得某个模式变成了现实。
但是想一想在语言中为模式的概念提供语法支持的含义:不必阅读“ 四人帮”一书,并记住特定模式的构造,而是在不可知论的情况下编写实现这种模式的代码,通用的方式一次,然后你就完成了! 然后,您可以重复使用这种模式,比如Visitor或者Strategy或者Façade或者其他任何东西,只需要在代码中用它来装饰操作,而不必一遍又一遍的重新实现!
这就是为什么懂得 monad的人会觉得他们如此有用 :知识分子势不两立地理解(不过,这当然也就是teehee),但这实际上使代码更简单。
(另请参阅什么是monad的答案? )
Monad的一个很好的动机是sigfpe(Dan Piponi)的你可以发明Monad! (也许你已经有) 。 还有很多其他monad教程 ,其中许多教程错误地试图用各种类比的“简单术语”来解释monad:这是Monad教程的谬误 ; 避免它们。
正如MacIver博士在告诉我们为什么你的语言很糟糕 :
所以,我讨厌Haskell的事情:
让我们从明显的开始。 Monad教程。 不,不是单子。 特别是教程。 他们是无尽的,夸张的,亲爱的上帝是他们乏味的。 而且,我从来没有看到有实际帮助的令人信服的证据。 阅读类的定义,写一些代码,克服可怕的名字。
你说你明白Maybe monad? 好,你在路上。 刚开始使用其他单子,迟早你会明白单子是一般的。
[如果你是math导向的,你可能要忽略几十个教程并学习定义,或者按照类别理论的讲座 :)定义的主要部分是Monad M涉及一个“types构造器”,它定义了每个现有types“T”是新types“MT”,以及“常规”types和“M”types之间来回的一些方式。
另外,令人惊讶的是,monad最好的介绍之一实际上是介绍monad的早期学术论文之一,Philip Wadler的Monads用于函数式编程 。 它实际上具有实际的, 不平凡的激励的例子,不像许多人造的教程那里。
A monad is, effectively, a form of "type operator". It will do three things. First it will "wrap" (or otherwise convert) a value of one type into another type (typically called a "monadic type"). Secondly it will make all the operations (or functions) available on the underlying type available on the monadic type. Finally it will provide support for combining its self with another monad to produce a composite monad.
The "maybe monad" is essentially the equivalent of "nullable types" in Visual Basic / C#. It takes a non nullable type "T" and converts it into a "Nullable<T>", and then defines what all the binary operators mean on a Nullable<T>.
Side effects are represented simillarly. A structure is created that holds descriptions of side effects alongside a function's return value. The "lifted" operations then copy around side effects as values are passed between functions.
They are called "monads" rather than the easier-to-grasp name of "type operators" for several reasons:
- Monads have restrictions on what they can do (see the definiton for details).
- Those restrictions, along with the fact that there are three operations involved, conform to the structure of something called a monad in Category Theory, which is an obscure branch of mathematics.
- They were designed by proponents of "pure" functional languages
- Proponents of pure functional languages like obscure branches of mathematics
- Because the math is obscure, and monads are associated with particular styles of programming, people tend to use the word monad as a sort of secret handshake. Because of this no one has bothered to invest in a better name.
After much striving, I think I finally understand the monad. After rereading my own lengthy critique of the overwhelmingly top voted answer, I will offer this explanation.
There are three questions that need to be answered to understand monads:
Why do you need a monad? What is a monad? How is a monad implemented?
As I noted in my original comments, too many monad explanations get caught up in question number 3, without, and before really adequately covering question 2, or question 1.
Why do you need a monad?
Pure functional languages like Haskell are different from imperative languages like C, or Java in that, a pure functional program is not necessarily executed in a specific order, one step at a time. A Haskell program is more akin to a mathematical function, in which you may solve the "equation" in any number of potential orders. This confers a number of benefits, among which is that it eliminates the possibility of certain kinds of bugs, particularly those relating to things like "state".
However, there are certain problems that are not so straightforward to solve with this style of programming. Some things, like console programming, and file i/o, need things to happen in a particular order, or need to maintain state. One way to deal with this problem is to create a kind of object that represents the state of a computation, and a series of functions that take a state object as input, and return a new modified state object.
so let's create a hypothetical "state" value, that represents the state of a console screen. exactly how this value is constructed is not important, but let's say it's an array of byte length ascii characters that represents what is currently visible on the screen, and an array that represents the last line of input entered by the user, in pseudocode. We've defined some functions that take console state, modify it, and return a new console state.
consolestate MyConsole = new consolestate;
so to do console programming, but in a pure functional manner, you would need to nest a lot of function calls inside eachother.
consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");
Programming in this way keeps the "pure" functional style, while forcing changes to the console to happen in a particular order. But, we'll probably want to do more than just a few operations at a time like in the above example. Nesting functions in that way will start to become ungainly. What we want, is code that does essentially the same thing as above, but is written a bit more like this:
consolestate FinalConsole = myconsole: print("Hello, what's your name?"): input(): print("hello, %inputbuffer%!");
this would indeed be a more convenient way to write it. How do we do that though?
What is a monad?
once you have a type (such as consolestate
) that you define along with a bunch of functions designed specifically to operate on that type, you can turn the whole package of these things into a "monad" by defining an operator like :
(bind) that automatically feeds return values on its left, into function parameters on its right, and a lift
operator that turns normal functions, into functions that work with that specific kind of bind operator.
How is a monad implemented?
See other answers, that seem quite free to jump into the details of that.
I wrote this mostly for me but I hope others find it useful 🙂
I believe this explanation is more correct. However, I think this treatment is still valuable and will contemplate incorporating it at a later time. Suffice it to say, whereas the conventional function composition deal with functions operating on object values, Monads is about providing composition to functions operating on function values. When you are dealing with a higher order functions (functions that accepts or return functions), the composition must be customize. Per the article above, that is the purpose of monads. With respect to the following content such higher order functions would be regarding the mathematical operators as functions accepting partially applied version of themselves 1+ ( 2* ( 3/ ( 7+ (..) ) ) )
…
Monads address a problem which also shows up in arithmetic as division by zero, DivByZero
. Specifically, calculations involving division must detect or allow for a DivByZero
exception. This requirement makes coding such expressions in the general case messy.
The Monadic solution is to embrace DivByZero
by doing the following
- Expand the
Number
type to includeDivByZero
as a specific value that is not a regular number:NaN
,Infinity
, orNull
. Let's call this new number type,Nullable<Number>
. - Provide a function for "lifting" or wrapping an existing
Number
into aNullable<Number>
type (the idea of "wrapping" is that the contentNumber
or value can be "unwrapped" without information loss) - Provide a function for "lifting" or wrapping existing operators on
Number
into a versions that operates onNullable<Number>
. Such a resultant "lifted" operator might merely do the following:- unwrap provided
Nullable<Number>
operands and apply its containedNumber
operator on them then "lift" the resultingNumber
result into aNullable<Number>
- detect a
DivByZero
operand or exception during evaluation and by pass further evaluation, producing aDivByZero
value as the result to assert that (1 + Null = Null
). However, what actions to take depends on the programmer. In general, these wrapper functions are where a lot of the functionality of Monads are written. The monadic state information is maintained within the wrapper type itself from where the wrapped functions inspect and, per functional programming immutability approach, construct a new monadic value. In the case ofNullable<Number>
, such a monadic state information would describe whetherDivByZero
or an actualNumber
exists.
- unwrap provided
So, a Monad is an expanded type together with a function that "wraps" the original type into this expanded version and another function that wraps the original operator(s) so they can handle this new expanded type. (Monads may have been a motivation for generics or type-parameters.)
It turns out that instead of merely smoothing out the handling of DivByZero
(or Infinity if you will), the Monad treatment is broadly applicable to situations that can benefit from type expansion to simplify their coding. In fact, this applicability seems to be wide.
For example, the IO Monad
is a type that represents the universe, literally. The intent is to recognize that the values returned by the prototypical HelloWorld
program is not completely described by the result type of string
and its value "Hello World!". In fact, such a result also includes modifications to hardware and memory states of devices such as the console. For instance, after execution the console is now displaying additional text, the cursor is on a new line, and so forth. The IO Monad
is merely an explicit recognition of such external effects or side effects, if you will.
何必?
Monads allow for strictly stateless algorithms to be devised and documenting state-full ones. State-full machines are complex. For example, a machine with only 10 bits may be in 2^10 possible states. Eliminating superfluous complexity is the ideal of functional languages.
Variables hold state. Eliminating "variables" should simply stuff. Purely functional programs don't handle variables, only values (despite usage of the term 'variable' in the Haskell documentation) and instead use labels or symbols or names for such values, as needed. Consequently, the closest thing to a variable in a purely functional language is the parameters received by a function as they accept new values on each invocation. (A label refers to a value whereas a variable refers to the place where a value is held. Consequently, you can modify the content of a variable but a label is the content itself. Ultimate it is better to be given an apple than a bag with an apple possibly in it.)
The absence of variables is why purely functional languages use recursion instead of loops to iterate. The act of incrementing a counter involves the use of a variable that becomes incremented and all the uncertainty with how it gets updated, when it gets tested, what value it should be and when, and then the complexity when you have multiple threads potentially accessing that same variable.
Nevertheless, So what?
Without the presence of state, a function must become a declaration or a definition of it's results, as oppose to a matriculation of some underlying state towards a result. Essentially, the functional expression of incFun(x) = x + 1
is simpler than the imperative expression of incImp(x) = x.add(1); return x;
Here, incFun
does not modify x
but creates a new value. incFun may even be replaced by its definition within expressions as in 1 + incFun(x)
becoming 1 + (x + 1)
. On the other hand, incImp
modifies the state of x
. Whatever such modification means for x
can be unclear and ultimately can be impossible to determine without executing the program in addition to any concurrency issues.
Such complexity gets cognitively expensive over time (2^N). In contrast, the operator, +
, cannot modify x
but must instead construct a new value whose result is limited to and fully determined by the values x
and 1
and the definition of +
. In particular, the 2^N complexity explosion is avoided. Additionally, to emphasize concurrency, incImp
, unlike incFun
, cannot be invoked concurrently without precautions around the sharing of the parameter since it becomes modified by each invocation.
Why call it a Monad?
A monad is characterized by a mathematical structure called a Monoid from Algebraic group theory. With that said, all it means is that a Monoid has the following three properties:
- has a binary operator,
*
, such thatx * y = z
forx, y, and z
belonging to some typeS
. For example 1 ÷ 2 = 0.5 where 1, 2, and 0.5 are all of typeNumber
. closures - has an identity element,
i
, associated with the binary operator that does nothing such that(i * x) = (x * i) = x
. For example the numeric operator, +, and the number, 0, in 4 + 0 = 0 + 4 = 4. Identity - the order of evaluation of "segments" is irrelevant:
(x * y) * z = x * (y * z)
. For example the numeric operator, +, in(3 + 4) + 12 = 3 + (4 + 12) = 19
. Note, however, that the sequencing of terms must not change. Associativity
Property three (Associativity) allows expressions of arbitrary lengths to be evaluated by delineating them into segments and evaluating each segment independently such as in parallel. For example, x1*x2*...*xN
may be segmented into (x1..xJ) * (xJ+1...xM) * (xM+1...xN)
. The separate result, x0J * xJM * xMN
, may then be collected and further evaluated similarly. Support for segmentation like this is a key technique ensuring correct concurrency and distributed evaluation as used by Google's distributed search algorithms (a la map/reduce).
Property two (Identity), allows for greater ease in constructing expressions in various ways though it may not be entirely obvious; however, in the same way that zero was not obviously necessary to early counting systems it is useful as a concept of empty as in to wrap an empty value. Note that in the type, Nullable<Number>
, Null
is not an empty value but rather DivByZero
. Specifically, nn + DivByZero = DivByZero
whereas nn + 0 = 0 + nn = nn
, hence 0
remains the identity under +
, where nn
is any Nullable<Number>
.
Finally, there is a reason we don`t use Roman Numerals anymore…no expanded accommodation for zero or fractions, irrational numbers, negative numbers, imaginary numbers,…yeah, it seems our number system can be considered a monad.
Monads are to control flow what abstract data types are to data.
In other words, many developers are comfortable with the idea of Sets, Lists, Dictionaries (or Hashes, or Maps), and Trees. Within those data types there are many special cases (for instance InsertionOrderPreservingIdentityHashMap).
However, when confronted with program "flow" many developers haven't been exposed to many more constructs than if, switch/case, do, while, goto (grr), and (maybe) closures.
So, a monad is simply a control flow construct. A better phrase to replace monad would be 'control type'.
As such, a monad has slots for control logic, or statements, or functions – the equivalent in data structures would be to say that some data structures allow you to add data, and remove it.
For example, the "if" monad:
if( clause ) then block
at its simplest has two slots – a clause, and a block. The if
monad is usually built to evaluate the result of the clause, and if not false, evaluate the block. Many developers are not introduced to monads when they learn 'if', and it just isn't necessary to understand monads to write effective logic.
Monads can become more complicated, in the same way that data structures can become more complicated, but there are many broad categories of monad that may have similar semantics, but differing implementations and syntax.
Of course, in the same way that data structures may be iterated over, or traversed, monads may be evaluated.
Compilers may or may not have support for user-defined monads. Haskell certainly does. Ioke has some similar capabilities, although the term monad is not used in the language.
My favorite Monad tutorial:
http://www.haskell.org/haskellwiki/All_About_Monads
(out of 170,000 hits on a Google search for "monad tutorial"!)
@Stu: The point of monads is to allow you to add (usually) sequential semantics to otherwise pure code; you can even compose monads (using Monad Transformers) and get more interesting and complicated combined semantics, like parsing with error handling, shared state, and logging, for example. All of this is possible in pure code, monads just allow you to abstract it away and reuse it in modular libraries (always good in programming), as well as providing convenient syntax to make it look imperative.
Haskell already has operator overloading[1]: it uses type classes much the way one might use interfaces in Java or C# but Haskell just happens to also allow non-alphanumeric tokens like + && and > as infix identifiers. It's only operator overloading in your way of looking at it if you mean "overloading the semicolon" [2]. It sounds like black magic and asking for trouble to "overload the semicolon" (picture enterprising Perl hackers getting wind of this idea) but the point is that without monads there is no semicolon, since purely functional code does not require or allow explicit sequencing.
This all sounds much more complicated than it needs to. sigfpe's article is pretty cool but uses Haskell to explain it, which sort of fails to break the chicken and egg problem of understanding Haskell to grok Monads and understanding Monads to grok Haskell.
[1] This is a separate issue from monads but monads use Haskell's operator overloading feature.
[2] This is also an oversimplification since the operator for chaining monadic actions is >>= (pronounced "bind") but there is syntactic sugar ("do") that lets you use braces and semicolons and/or indentation and newlines.
I've been thinking of Monads in a different way, lately. I've been thinking of them as abstracting out execution order in a mathematical way, which makes new kinds of polymorphism possible.
If you're using an imperative language, and you write some expressions in order, the code ALWAYS runs exactly in that order.
And in the simple case, when you use a monad, it feels the same — you define a list of expressions that happen in order. Except that, depending on which monad you use, your code might run in order (like in IO monad), in parallel over several items at once (like in the List monad), it might halt partway through (like in the Maybe monad), it might pause partway through to be resumed later (like in a Resumption monad), it might rewind and start from the beginning (like in a Transaction monad), or it might rewind partway to try other options (like in a Logic monad).
And because monads are polymorphic, it's possible to run the same code in different monads, depending on your needs.
Plus, in some cases, it's possible to combine monads together (with monad transformers) to get multiple features at the same time.
In addition to the excellent answers above, let me offer you a link to the following article (by Patrick Thomson) which explains monads by relating the concept to the JavaScript library jQuery (and its way of using "method chaining" to manipulate the DOM): jQuery is a Monad
The jQuery documentation itself doesn't refer to the term "monad" but talks about the "builder pattern" which is probably more familiar. This doesn't change the fact that you have a proper monad there maybe without even realizing it.
Monads Are Not Metaphors , but a practically useful abstraction emerging from a common pattern, as Daniel Spiewak explains.
I am still new to monads, but I thought I would share a link I found that felt really good to read (WITH PICTURES!!): http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/ (no affiliation)
Basically, the warm and fuzzy concept that I got from the article was the concept that monads are basically adapters that allow disparate functions to work in a composable fashion, ie be able to string up multiple functions and mix and match them without worrying about inconsistent return types and such. So the BIND function is in charge of keeping apples with apples and oranges with oranges when we're trying to make these adapters. And the LIFT function is in charge of taking "lower level" functions and "upgrading" them to work with BIND functions and be composable as well.
I hope I got it right, and more importantly, hope that the article has a valid view on monads. If nothing else, this article helped whet my appetite for learning more about monads.
A monad is a way of combining computations together that share a common context. It is like building a network of pipes. When constructing the network, there is no data flowing through it. But when I have finished piecing all the bits together with 'bind' and 'return' then I invoke something like runMyMonad monad data
and the data flows through the pipes.
The two things that helped me best when learning about there were:
Chapter 8, "Functional Parsers," from Graham Hutton's book Programming in Haskell . This doesn't mention monads at all, actually, but if you can work through chapter and really understand everything in it, particularly how a sequence of bind operations is evaluated, you'll understand the internals of monads. Expect this to take several tries.
The tutorial All About Monads . This gives several good examples of their use, and I have to say that the analogy in Appendex I worked for me.
Monoid appears to be something that ensures that all operations defined on a Monoid and a supported type will always return a supported type inside the Monoid. Eg, Any number + Any number = A number, no errors.
Whereas division accepts two fractionals, and returns a fractional, which defined division by zero as Infinity in haskell somewhy(which happens to be a fractional somewhy)…
In any case, it appears Monads are just a way to ensure that your chain of operations behaves in a predictable way, and a function that claims to be Num -> Num, composed with another function of Num->Num called with x does not say, fire the missiles.
On the other hand, if we have a function which does fire the missiles, we can compose it with other functions which also fire the missiles, because our intent is clear — we want to fire the missiles — but it won't try printing "Hello World" for some odd reason.
In Haskell, main is of type IO (), or IO [()], the distiction is strange and I will not discuss it but here's what I think happens:
If I have main, I want it to do a chain of actions, the reason I run the program is to produce an effect — usually though IO. Thus I can chain IO operations together in main in order to — do IO, nothing else.
If I try to do something which does not "return IO", the program will complain that the chain does not flow, or basically "How does this relate to what we are trying to do — an IO action", it appears to force the programmer to keep their train of thought, without straying off and thinking about firing the missiles, while creating algorithms for sorting — which does not flow.
Basically, Monads appear to be a tip to the compiler that "hey, you know this function that returns a number here, it doesn't actually always work, it can sometimes produce a Number, and sometimes Nothing at all, just keep this in mind". Knowing this, if you try to assert a monadic action, the monadic action may act as a compile time exception saying "hey, this isn't actually a number, this CAN be a number, but you can't assume this, do something to ensure that the flow is acceptable." which prevents unpredictable program behavior — to a fair extent.
It appears monads are not about purity, nor control, but about maintaining an identity of a category on which all behavior is predictable and defined, or does not compile. You cannot do nothing when you are expected to do something, and you cannot do something if you are expected to do nothing (visible).
The biggest reason I could think of for Monads is — go look at Procedural/OOP code, and you will notice that you do not know where the program starts, nor ends, all you see is a lot of jumping and a lot of math,magic,and missiles. You will not be able to maintain it, and if you can, you will spend quite a lot of time wrapping your mind around the whole program before you can understand any part of it, because modularity in this context is based on interdependant "sections" of code, where code is optimized to be as related as possible for promise of efficiency/inter-relation. Monads are very concrete, and well defined by definition, and ensure that the flow of program is possible to analyze, and isolate parts which are hard to analyze — as they themselves are monads. A monad appears to be a "comprehensible unit which is predictable upon its full understanding" — If you understand "Maybe" monad, there's no possible way it will do anything except be "Maybe", which appears trivial, but in most non monadic code, a simple function "helloworld" can fire the missiles, do nothing, or destroy the universe or even distort time — we have no idea nor have any guarantees that IT IS WHAT IT IS. A monad GUARANTEES that IT IS WHAT IT IS. which is very powerful.
All things in "real world" appear to be monads, in the sense that it is bound by definite observable laws preventing confusion. This does not mean we have to mimic all the operations of this object to create classes, instead we can simply say "a square is a square", nothing but a square, not even a rectangle nor a circle, and "a square has area of the length of one of it's existing dimensions multiplied by itself. No matter what square you have, if it's a square in 2D space, it's area absolutely cannot be anything but its length squared, it's almost trivial to prove. This is very powerful because we do not need to make assertions to make sure that our world is the way it is, we just use implications of reality to prevent our programs from falling off track.
Im pretty much guaranteed to be wrong but I think this could help somebody out there, so hopefully it helps somebody.
In practice, monad is a custom implementation of function composition operator that takes care of side effects and incompatible input and return values (for chaining).
This answer begins with a motivating example, works through the example, derives an example of a monad, and formally defines "monad".
Consider these three functions in pseudocode:
f(<x, messages>) := <x, messages "called f. "> g(<x, messages>) := <x, messages "called g. "> wrap(x) := <x, "">
f
takes an ordered pair of the form <x, messages>
and returns an ordered pair. It leaves the first item untouched and appends "called f. "
to the second item. Same with g
.
You can compose these functions and get your original value, along with a string that shows which order the functions were called in:
f(g(wrap(x))) = f(g(<x, "">)) = f(<x, "called g. ">) = <x, "called g. called f. ">
You dislike the fact that f
and g
are responsible for appending their own log messages to the previous logging information. (Just imagine for the sake of argument that instead of appending strings, f
and g
must perform complicated logic on the second item of the pair. It would be a pain to repeat that complicated logic in two — or more — different functions.)
You prefer to write simpler functions:
f(x) := <x, "called f. "> g(x) := <x, "called g. "> wrap(x) := <x, "">
But look at what happens when you compose them:
f(g(wrap(x))) = f(g(<x, "">)) = f(<<x, "">, "called g. ">) = <<<x, "">, "called g. ">, "called f. ">
The problem is that passing a pair into a function does not give you what you want. But what if you could feed a pair into a function:
feed(f, feed(g, wrap(x))) = feed(f, feed(g, <x, "">)) = feed(f, <x, "called g. ">) = <x, "called g. called f. ">
Read feed(f, m)
as "feed m
into f
". To feed a pair <x, messages>
into a function f
is to pass x
into f
, get <y, message>
out of f
, and return <y, messages message>
.
feed(f, <x, messages>) := let <y, message> = f(x) in <y, messages message>
Notice what happens when you do three things with your functions:
First: if you wrap a value and then feed the resulting pair into a function:
feed(f, wrap(x)) = feed(f, <x, "">) = let <y, message> = f(x) in <y, "" message> = let <y, message> = <x, "called f. "> in <y, "" message> = <x, "" "called f. "> = <x, "called f. "> = f(x)
That is the same as passing the value into the function.
Second: if you feed a pair into wrap
:
feed(wrap, <x, messages>) = let <y, message> = wrap(x) in <y, messages message> = let <y, message> = <x, ""> in <y, messages message> = <x, messages ""> = <x, messages>
That does not change the pair.
Third: if you define a function that takes x
and feeds g(x)
into f
:
h(x) := feed(f, g(x))
and feed a pair into it:
feed(h, <x, messages>) = let <y, message> = h(x) in <y, messages message> = let <y, message> = feed(f, g(x)) in <y, messages message> = let <y, message> = feed(f, <x, "called g. ">) in <y, messages message> = let <y, message> = let <z, msg> = f(x) in <z, "called g. " msg> in <y, messages message> = let <y, message> = let <z, msg> = <x, "called f. "> in <z, "called g. " msg> in <y, messages message> = let <y, message> = <x, "called g. " "called f. "> in <y, messages message> = <x, messages "called g. " "called f. "> = feed(f, <x, messages "called g. ">) = feed(f, feed(g, <x, messages>))
That is the same as feeding the pair into g
and feeding the resulting pair into f
.
You have most of a monad. Now you just need to know about the data types in your program.
What type of value is <x, "called f. ">
? Well, that depends on what type of value x
is. If x
is of type t
, then your pair is a value of type "pair of t
and string". Call that type M t
.
M
is a type constructor: M
alone does not refer to a type, but M _
refers to a type once you fill in the blank with a type. An M int
is a pair of an int and a string. An M string
is a pair of a string and a string. 等等。
Congratulations, you have created a monad!
Formally, your monad is the tuple <M, feed, wrap>
.
A monad is a tuple <M, feed, wrap>
where:
-
M
is a type constructor. -
feed
takes a (function that takes at
and returns anM u
) and anM t
and returns anM u
. -
wrap
takes av
and returns anM v
.
t
, u
, and v
are any three types that may or may not be the same. A monad satisfies the three properties you proved for your specific monad:
-
Feeding a wrapped
t
into a function is the same as passing the unwrappedt
into the function.Formally:
feed(f, wrap(x)) = f(x)
-
Feeding an
M t
intowrap
does nothing to theM t
.Formally:
feed(wrap, m) = m
-
Feeding an
M t
(call itm
) into a function that- passes the
t
intog
- gets an
M u
(call itn
) fromg
- feeds
n
intof
是相同的
- feeding
m
intog
- getting
n
fromg
- feeding
n
intof
Formally:
feed(h, m) = feed(f, feed(g, m))
whereh(x) := feed(f, g(x))
- passes the
Typically, feed
is called bind
(AKA >>=
in Haskell) and wrap
is called return
.
If I've understood correctly, IEnumerable is derived from monads. I wonder if that might be an interesting angle of approach for those of us from the C# world?
For what it's worth, here are some links to tutorials that helped me (and no, I still haven't understood what monads are).
In the context of Scala you will find the following to be the simplest definition. Basically flatMap (or bind) is 'associative' and there exists an identity.
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 } }
例如
// 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)
NOTE Strictly speaking the definition of a Monad in functional programming is not the same as the definition of a Monad in Category Theory , which is defined in turns of map
and flatten
. Though they are kind of equivalent under certain mappings. This presentations is very good: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category
Explaining monads seems to be like explaining control-flow statements. Imagine that a non-programmer asks you to explain them?
You can give them an explanation involving the theory – Boolean Logic, register values, pointers, stacks, and frames. But that would be crazy.
You could explain them in terms of the syntax. Basically all control-flow statements in C have curly brackets, and you can distinguish the condition and the conditional code by where they are relative to the brackets. That may be even crazier.
Or you could also explain loops, if statements, routines, subroutines, and possibly co-routines.
Monads can replace a fairly large number of programming techniques. There's a specific syntax in languages that support them, and some theories about them.
They are also a way for functional programmers to use imperative code without actually admitting it, but that's not their only use.
In the Coursera "Principles of Reactive Programming" training – Erik Meier describes them as:
"Monads are return types that guide you through the happy path." -Erik Meijer
What the world needs is another monad blog post, but I think this is useful in identifying existing monads in the wild.
- monads are fractals
The above is a fractal called Sierpinski triangle, the only fractal I can remember to draw. Fractals are self-similar structure like the above triangle, in which the parts are similar to the whole (in this case exactly half the scale as parent triangle).
Monads are fractals. Given a monadic data structure, its values can be composed to form another value of the data structure. This is why it's useful to programming, and this is why it occurrs in many situations.
A monad is a thing used to encapsulate objects that have changing state. It is most often encountered in languages that otherwise do not allow you to have modifiable state (eg, Haskell).
An example would be for file I/O.
You would be able to use a monad for file I/O to isolate the changing state nature to just the code that used the Monad. The code inside the Monad can effectively ignore the changing state of the world outside the Monad – this makes it a lot easier to reason about the overall effect of your program.
http://code.google.com/p/monad-tutorial/ is a work in progress to address exactly this question.
Princess 's explanation of F# Computation Expressions helped me, though I still can't say I've really understood.
EDIT : this series – explaining monads with javascript – is the one that 'tipped the balance' for me.
-
http://blog.jcoglan.com/2011/03/06/monad-syntax-for-javascript/
-
http://blog.jcoglan.com/2011/03/11/promises-are-the-monad-of-asynchronous-programming/
I think that understanding monads is something that creeps up on you. In that sense, reading as many 'tutorials' as you can is a good idea, but often strange stuff (unfamiliar language or syntax) prevents your brain from concentrating on the essential.
Some things that I had difficulty understanding:
- Rules-based explanations never worked for me, because most practical examples actually require more than just return/bind.
- Also, calling them rules didn't help. It is more a case of "there are these things that have something in common, let's call the things 'monads', and the bits in common 'rules'".
- Return (
a -> M<a>
) and Bind (M<a> -> (a -> M<b>) -> M<b>
) are great, but what I could never understand is HOW Bind could extract thea
fromM<a>
in order to pass it intoa -> M<b>
. I don't think I've ever read anywhere (maybe it's obvious to everyone else), that the reverse of Return (M<a> -> a
) has to exist inside the monad, it just doesn't need to be exposed.