论坛首页 综合技术论坛

开始翻译 All About Monads

浏览 8340 次
精华帖 (4) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-12-03  
学了一年多haskell了, 对monad依然很是头疼,翻译是最好的学习,所以开始翻译这篇教程。也算为国内贡献份文裆。

刚开始,翻了头两章,以后争取两天一章的进度。

http://www.kamang.net/node/144
   发表时间:2007-12-03  
All About Monads (翻译)
Mon, 12/03/2007 - 11:27 — albertlee
译注:
学习haskell超过一年了,还是对monad一头雾水,人家说如果能把monad完全讲明白可以去当大学教授了。人家还说翻译是最好的学习。所以我就开始翻译这篇经典的monad教程。行文较为粗放,谬误在所难免,望读者指正。

Haskell monadic编程理论与实践指导

原文地址: http://www.haskell.org/all_about_monads/html/index.html

这份教程目标在于给初学及中等水平的Haskell程序员一个简单的方式解释monad的概念和其在函数式编程中的应用。需要先熟悉haskell语法,但对monad没有预先要求。教程涵盖了非常多的内容,后面的部分需要对前面的内容有完整的理解。提供很多代码例子用来演示monadic编程。不建议试图一次读完。(译注:读很多次都不一定够……)

教程分三部分。第一部分给出对monad在函数式编程中所扮演的角色的一个基本理解,解释了它是什么,如何操作,如何声明和如何在 haskell中使用。第二部分包括了Haskell中的每一个标准monad,给出其声明并讨论其应用。第三部分包括一些transformer相关的高级内容, 以及实际编程中会遇到一些问题。

要实际理解monad最好的办法是实际试验一些monadic的代码。这份教程包含了很多例子代码,涵盖了简单到适度复杂的例子,读者最好去研究把玩并修改他们。不幸的是,起先的例子有必要故意设计的不去组合多个monad。如果你觉得这样有些麻烦,可以提前刊第三部分来学习如何解决。
0 请登录后投票
   发表时间:2007-12-03  
Introduction Monad介绍
Mon, 12/03/2007 - 11:35 — albertlee
What is a monad? 什么是Monad?
译注:这里的“计算”可以理解为程序,或者说程序会更顺口些,不过原文是computation,含义应与传统的程序相区分,故采用“计算”的翻译。

一个monad是用值和使用这些值的计算序列来构建计算的方法。Monad允许 程序员使用序列的块来构建计算,这些序列的块本身也可以是序列的计算。Monad来决定如何组合这些计算来构建一个新的计算,程序员不需要每次手工编写组合的程序。

可以把monad想象成把计算组合成更复杂的计算的策略。比如,你应该对Haskell中的 Maybe这个类型很熟悉:

data Maybe a = Nothing | Just a

Maybe表示了一种在返回值时有可能会失败的计算类型。返回Maybe类型值得的组合计算暗示了一种策略:如果一个组合的计算包括一个计算B和一个计算A,其中B计算依赖于A计算的结果,如果A或者B返回Nothing,那么组合组合的计算返回Nothing,如果A和B计算都成功,则这个组合计算返回B计算的值。
其他的monad存在于使用I/O的,有状态的,可能返回多个值的等等的计算中。有多少种计算的策略就会有多少种不同类型的monad,不过有一些特殊的monad尤其的有用并且足够通用,他们包含在 Haskell98 标准库中。这些Monad将在本教程的第二部分中分别介绍。

Why should I make the effort to understand monads?
我为什么要努力理解Monad?
网上存在的关于 monad的教程的绝对数量反应了很多人对这个问题的难度大理解。(译注:真的很多,我也看了很多,还是不理解)这是因为monad的抽象实质,以及一些使用它的不同方式,这会造成一副很令人混淆的图景,让人们不清楚究竟Monad是什么,适合干什么。
Haskell中,monad在IO系统里扮演了核心角色。理解haskell中使用monad进行IO并非基本要求,不过理解了IO monad将改进代码并扩展你的能力。
对程序员来说,monad是构建函数式程序的有用工具。他们具有的三个属性使他们特别有用:

模块性:可以使用简单的计算组成复杂的计算,并且将组合策略与实际的计算相隔离。
适应性:使用monad的函数式程序可以比不使用monad的等价程序具有更好地适应性。因为monad把计算的策略提取到一个单独的地方而不是分布在整个程序的各处。
隔离性:可以用来编写命令式风格的程序,并与函数式风格的程序主体安全的隔离。这使得纯函数式的语言如Haskell可以应用“副作用”(如IO)以及状态(状态的使用违反了引用透明性)。
后面在讲具体的monad时会回头来重新提到这些特性。
0 请登录后投票
   发表时间:2007-12-03  
http://www.kamang.net/node/146

Meet the Monads 初识Monad
Mon, 12/03/2007 - 15:30 — albertlee
Type constructors
Maybe a monad
A list is also a monad
An example
Summary
这一章将使用 Maybe类型, 继续阅读前最好先熟悉Maybe的定义与使用。

Type constructors 类型构建子
为了理解 Haskell中的 Monad,你需要对类型构建子感到适应。一个类型构建子是一个使用了多态的参数化类型定义。Haskell中, 给一个类型构建子一个具体类型后,这个构建子就可以构造出一个新的具体的类型。 在Maybe类型的定义中:
data Maybe a = Nothing | Just a
Maybe是一个类型构建子,Nothing和Just是data构建子。你可以给 Just构造子应用一个值来构造一个Maybe的值:
country = Just "China" (译注:原文就是 China,不知道作者与中国有何关联)
同样,你可以给Maybe类型应用一个类型来构建一个新的类型:
lookupAge :: DB -> String -> Maybe Int

多态类型就像一个可以装不同类型的值得容器。Maybe Int 可以想象成一个可以装 Int 值(或者 Nothing) 的Maybe容器, Maybe String 是可以装String值(或者Nothing)的Maybe容器。在Haskell中,我们也可以把容器本身也多态,所以我们可以写成 "m a" 来表示一个某种类型的容器装了某种类型的值!
(译注: Maybe a 将 Maybe 用 m 代替,就成了 m a)

我们经常使用类型构建子和类型变量来描述一个计算的抽象属性。比如,多态类型 Maybe a 是所有会返回一个值或不返回值的计算的类型。在这种情况下,我们可以抛开容器中包含的内容细节而谈论这个容器的属性。
如果使用monad的时候遇到编译器错误提示“kind errors" ,说明你在使用类型构建子的时候不正确。

Maybe a monad
Haskell中一个monad表示成类型构造子(称为m);一个函数来构造这种类型(a->m a);一个函数来把这种类型(m a)的值与一个使用a类型产生m a类型的计算来组合起来。(译注,这一点比较绕,看函数的类型定义体会: (m a -> (a -> m b) -> m b) )一般习惯上涨讨论monad的时候把类型构造子称为"m"。把值构造成一个monad类型的函数传统上称为“return”,第三个函数是“bind”不过写作">>="。函数签名如下:
-- monad m 类型
data m a = ...
-- return 创建一个monad数据的实例
return :: a -> m a
-- bind组合一个monad实例与一个产生另一个monad实例的函数
(>>=) :: m a -> (a -> m b) -> m b

(译注:这一段翻译的比较绕,实际上,我强烈推荐大家看原文,自己体会,毕竟教程开头就说了,不期望读一遍就明白,保留原文如下:)
In Haskell a monad is represented as a type constructor (call it m), a function that builds values of that type (a -> m a), and a function that combines values of that type with computations that produce values of that type to produce a new computation for values of that type (m a -> (a -> m b) -> m b). Note that the container is the same, but the type of the contents of the container can change. It is customary to call the monad type constructor "m" when discussing monads in general. The function that builds values of that type is traditionally called "return" and the third function is known as "bind" but is written ">>=". The signatures of the functions are:

粗略的说,monad类型构建子定义了一个计算的类型,return 函数创建这个计算类型的一个元素, >>= bind 把这个类型的计算组合中一起来构造这种类型的更加复杂的计算。用容器来做类比,娄星构建子m是一个容器可以装不同的值。 M a 是一个装a类型值的容器。Return 函数把一个值装进这个容器。>>= 函数把值从monad容器中取出来,传给一个函数产生一个装载新值的monad容器(值类型可能不同)。>>= 也叫做 "bind" 函数因为它把monad容器中的值绑定到一个函数的第一个参数上。给bind函数增加逻辑,一个monad可以实现特别的组合计算策略。
看来下面的例子就清楚了, 如果还是觉得特别糊涂的话,可以先看看这个物理类比:
http://www.haskell.org/all_about_monads/html/analogy.html

An example
假设我们写个程序来对克隆羊实验进行跟踪。我们当然想知道我们的每只羊的基因历史,因此我们需要 mother, father 函数。但是,因为是克隆羊,因此他们不是总有mother和father 函数。
我们在Haskell程序中用 Maybe 类型来表示不含有mother 或 father的可能性:
(译注:我非常怀疑原作者非常想用人来做例子,那样确实自然多了)

type Sheep = ...

father :: Sheep -> Maybe Sheep
father = ...

mother :: Sheep -> Maybe Sheep
mother = ...

查找外祖父的函数有一点复杂,因为我们要处理没有母亲的可能:

maternalGrandfather :: Sheep -> Maybe Sheep
maternalGrandfather s = case (mother s) of
                          Nothing -> Nothing
                          Just m  -> father m
继续其他的组合函数:我们会发现要求曾祖父更加的麻烦:

mothersPaternalGrandfather :: Sheep -> Maybe Sheep
mothersPaternalGrandfather s = case (mother s) of
                                 Nothing -> Nothing
                                 Just m  -> case (father m) of
                                              Nothing -> Nothing
                                              Just gf -> father gf
要避免这样丑陋的、不清楚的、难于维护的代码,需要很多工作。显然计算中任何点出现Nothing值,整个计算结果就是Nothing,如果把这个观念抽出来放到一个单独的地方,会比它分散在代码各处单独测试好的多。这将会使得代码更容易编写、阅读与修改。好的编程风格是我们创建combinator 来捕获我们希望的行为。(代码示例 example1.hs)
(译注: 果然,作者确实是想拿人举例子的,证据就在 example1.hs里)

-- comb is a combinator for sequencing operations that return Maybe
comb :: Maybe a -> (a -> Maybe b) -> Maybe b
comb Nothing  _ = Nothing
comb (Just x) f = f x

-- now we can use `comb` to build complicated sequences
mothersPaternalGrandfather :: Sheep -> Maybe Sheep
mothersPaternalGrandfather s = (Just s) `comb` mother `comb` father `comb` father 

Combinator非常成功!代码要干净的多,也更容易写更容易理解和修改。注意 comb 函数也是完全多态的--它并不是只特定对 Sheep的。实际上,这个 combinator 捕获了可能会返回失败的计算的一个通用的策略。这样,我们也可以用同样的combinator来应用到其他可能会返回失败的计算中,比如数据库查询或者字典查找。
一个很开心的结果一个普通的程序实践指引我们构建了一个monad,甚至我们没有特地实现它。Maybe类型构建子及Just函数(扮演return的角色)和我们的 combinator(扮演 >>=)一起组成了一个简单的monad,这个monad可以来建立可能会返回失败的计算。剩下的事情就是让这个monad可以和haskell中的monad框架和谐共处。这是下一章的主题。

A list is also a monad List也是一个Monad
你已经看到了Maybe类型构造子是用来构造可能会返回失败的计算的Monad。你可能会很惊讶另外一个很通用的Haskell类型 [] (list列表)也是一个monad。List Monad可以让我们构建一个可以返回0,1 或更多值的计算。
List 的 return函数简单的创建一个单元素的列表(return x = [x])。List的 bind操作把函数应用到列表原先的所有值上,创建一个新的列表(l >>= f = concatMap f l)。
返回列表的函数的一个应用是表示非确定计算--产生0,1或更多可能值得计算。在一个由非确定子计算构成的计算中,非确定性可以混合,或最终解出一个可能的结果或者根本没结果。在这个过程中,计算的可能状态表示为一个列表。List Monad 表达了一种多种可能的非确定计算路径同时计算的策略。
List Monad的这种应用的例子,以及相对的Maybe monad使用的例子不久将给出。不过首先让我们看一下Haskell中定义的monad多么有用。

Summary
我们看到了,一个monad是一个类型构建子,一个return函数,和一个叫bind(>>=)的组合函数。这三个元素一起工作来封装一个构造更复杂计算的的组合策略。
使用Maybe类型构建子,我们看到了好的编程实践让我们定义了一个简单的monad,可以用来构建复杂的一串可能失败的计算。结果Maybe monad封装了一个可能不返回值的计算策略。通过把这个策略编码到 monad中,我们获得了比用ad-hoc方式组合计算更具模块性和灵活性的方法。
我们也看到了另外一个通用的Haskell数据[],也是一个monad。List monad封装了可以返回0,1后者更多值得一个组合计算的策略。
0 请登录后投票
   发表时间:2007-12-04  
第三章 Doing it with class

Tue, 12/04/2007 - 11:03 — albertlee
Haskell type classes
The Monad class
Example continued
Do notation
Summary

Haskell type classes
本章的讨论涉及到Haskell的类型class系统。如果你对haskll中的class 不熟悉,需要先回顾一下这部分。

The Monad class
在haskell中,有一个标准的Monad class,它定义了两个monad中的函数原型return , >>=。你的monad实例并不是一定要继承自Monad class,不过最好如此。Haskell语言内建了对Monad class的特殊支持,使得你的monad实例的代码更清楚更优雅。并且使用不标准的函数名时可以给用户信息(?)。它很简单易用,而且有很多好处,所以用它好了。
Haskell中的标准 Monad class 定义类似这样:

class Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    return :: a -> m a
Example continued
继续上面的例子,我们来看看 Maybe 类型构建子如何通过继承 Monad class 来适应Haskell monad系统。
回想我们的Maybe monad使用 Just数据构建子来充当monad return函数的角色,并且构造了一个简单的combinator来充当 monad >>= bind函数的角色。我们可以把Maybe声明成 Monad class的实例来明确他们的角色:

instance Monad Maybe where
    Nothing  >>= f = Nothing
    (Just x) >>= f = f x
    return         = Just
一旦我们把Maybe定义成Monad class的实例,我们就可以使用标准的monad操作来构造复杂的计算:
-- 我们可以使用monad风格从操作符

maternalGrandfather :: Sheep -> Maybe Sheep
maternalGrandfather s = (return s) >>= mother >>= father

fathersMaternalGrandmother :: Sheep -> Maybe Sheep
fathersMaternalGrandmother s = (return s) >>= father >>= mother >>= mother 

在haskell的标准prelude模块中,Maybe定义为Monad class的实例,因此你无需自己来做。其他我们已经见到的monad,list也是在prelude中定义为Monad class的实例。
在写使用monad的函数时,试着使用Monad class 而不是用特定的monad实。一个
doSomething :: (Monad m) => a -> m b 这样类型的函数比 doSomething :: a-> Maybe b 这样类型的函数更灵活。前面那个函数可以通过嵌入 monad中的不同策略来获得不同的行为,而后面那个被Maybe monad限制了特定的策略。

Do notation
使用标准的monad函数名很好,另一个用monad class的好处是 Haskell 支持的 “do”语法形式。 “do”形式是一个构建monadic计算的简写,就像列表内涵式(list comperhension)是建立列表计算的简写一样。Haskell中Monad class的每一个实例都可以使用do程序块。
简单讲,do形式允许用一种带有命名变量的伪-命令式风格来写monadic计算。一个monadic计算的结果可以通过 <- 操作符“分配”给一个变量。然后自动bind这个变量用在后面的 monadic计算中。<- 右边的表达式的类型是一个monad类型 m a。<-左边的表达式是一个模式,来匹配monad内部的值。(x:xs)可以匹配 Maybe [1,2,3], 比如:
这里是一个Maybe monad使用do形式的例子:代码: example2.hs
-- 我们可以用do-形式来建立复杂的顺序计算

mothersPaternalGrandfather :: Sheep -> Maybe Sheep
mothersPaternalGrandfather s = do m  <- mother s
                                  gf <- father m
                                  father gf
与不使用do形式的 fathersMaternalGrandmother 比较一下。
上面的 do 程序块用的layout布局定义的方式。Haskell也允许使用大括号和分号来定义do程序块。

mothersPaternalGrandfather s = do { m <- mother s; gf <- father m; father gf }
注意 do形式类似于命令式程序语言,在其中的计算是由一串相似的计算显式的串行的而成。从这个角度看,monad提供了一种在大地函数式程序中使用命令式程序的可能。当我们后面处理IO和副作用的时候还要展开这个主题。
Do形式是一个简单的语法糖。没有什么do可以做而只用标准monad操作符无法完成的。不过do形式更加清晰,在某些情况下也更方便,尤其是monad计算序列很长的时候。 你要理解标准的monad bind记号和do形式,并可以在合适的情况中应用每一个。
从do形式到monad操作符的实际转换,粗略的说说每一个匹配一个模式的表达式 x<- expr1 变为: expr1 >>= \x -> ,每一个没有变量赋值的表达式 expr2 变为 expr2 >>= \_ -> 。
所有的 do 块最后以一个 monad 表达式结束,可以在do块开始的时候使用 let 子句(do中的 let子句不使用“in”关键字)。上面的 mothersPaternalGrandfather 函数定义可以转换成:

mothersPaternalGrandfather s = mother s >>= \m ->
                               father m >>= \gf ->
                               father gf

(译注: \x -> 是 lambda函数的生命方法, \_ -> 则是一个忽略传入参数的lambda函数,这个函数相当于 bind 函数声明中 (m a -> m b) 这个参数)

现在清楚了为什么 bind 操作符如此命名了。它是把monad中的值绑定到随后的lambda函数表达式的参数的字面意思。

Summary
Haskell提供了内建monad的内建支持。要获得Haskell的monad支持的优势,你需要把你的monad类型声明成Monad class的实例,并提供 return 和 >>= (读为“bind”)的定义。
Monad class的实例monad可以使用do-形式,这种“语法糖”提供了简单的,命令式风格的记号来描述monad的计算。
0 请登录后投票
   发表时间:2007-12-12  
第四章  The monad laws
关于 Monad 的数学基础,请看这里:
http://www.iteye.com/topic/147443

The three fundamental laws
Failure IS an option
No way out
Zero and Plus
Summary

这份教程到现在为止一直避免纯技术的讨论,不过有一些monad的技术点必须说。Monad必须遵从一些“Monad公理”。Haskell编译器并不保证这些公理,必须由程序员来保证他们声明的Monad实例遵从这些法则。Haskell中的Monad类还包括了一些我们没看到超出最小完整monad定义的功能。有很多Monad遵守标准monad规则之外的附加规则,有一个附加的Haskell类来支持这些扩展的Monad.
The three fundamental laws
Monad的概念来自一个叫“范畴论”的数学分支。 尽管如此,我们创建与使用monad并不一定要知道范畴论,我们只需要遵从很少的一些数学形式。要创建一个monad,不只是在Haskell中声明一个带有正确类型原形的Monad类的实例。要成为一个合适的monad, return 和 >>= 函数必须按如下三个规则一起工作:
(return x) >>= f == f x
m >>= return == m
(m >>= f) >>= g == m >>= (\x -> f x >>= g)

(借用 st_monad 的说法:
第一个,monad bind到constructor等价于直接apply monad中的pure部分到constructor。
第二个,return保留monad的所有信息不变。
第三个,bind运算满足结合律。

规则1规定return作为>>=的左单位元,规则2规定return作为>>=的右单位元;规则三规定了bind 运算的结合律。满足这三条规则可以保证使用monad的do形式在语义上的一致性。
任何一个带有return和bind操作符并满足这三条规则的类型构建子就是一个monad。在Haskell中,编译器不会检查Monad类的每个实例是否遵守这些规则。这要由程序员来保证他创建的每一个Monad实例都满足这些规则。
Failure IS an option
之前给出的Monad类定义只是一个最小完备实现。实际上完整的Monad类定义还包括两个附加的函数: fail 和 >>。
默认的 fail 函数实现是:
fail s = error s
你的monad不需要改变这个定义,除非你想给失败提供不同的行为或者把失败混合进你的monad的计算策略中。比如Maybe monad,把 fail 定义为:

fail _ = Nothing

这样在Maybe monad 中当它与其他函数 bind 时,fail 将返回一个有意义的 Maybe monad的实例。(?这句翻的很别扭,有意义,就是好好活,好好活就是做很多有意义的事)
fail 函数在 monad 的数学定义中并不是一个必须的部分,但它由于它在Haskell do形式中扮演的角色,所以他包含在标准Monad类定义中。在 do 语句块中,一旦模式匹配失败就会调用fail函数。


fn :: Int -> Maybe [Int]
fn idx = do let l = [Just [1,2,3], Nothing, Just [], Just [7..20]]
            (x:xs) <- l!!idx   -- a pattern match failure will call "fail"
            return xs


在上面的代码中, fn 0 返回 Just [2, 3], 但是fn 1 和 fn 2都返回 Nothing。

>> 函数是一个简便函数,用于计算序列中,bind一个不需要前面计算结果的monadic计算。用>>=来定义:

(>>) :: m a -> m b -> m b
m >> k = m >>= (\_ -> k)


No way out
(被T1老大鞭策之,不能惰遁)
你可能已经注意到了,没有办法把在标准的Monad类中的值取出Monad。这并不意外。并没有什么阻止monad的作者去用针对monad的函数。比如,可以用模式匹配 Just x 或 fromJust函数来吧 Maybe monad 中的值抽取出来。
通过不提供这样的函数,Haskell Monad类允许创建“单向”Monad。单向monad允许通过 return (有时是 fail)把值放入 monad,并且允许在monad中用 >>= 和 >>执行计算,但是不允许把值传出Monad。
IO monad是 Haskell中一个常见的单向monad的例子。因为你无法从IO monad中逃离,不可能写一个在 IO monad中进行了计算但结果的类型里不包括IO类型构造子的函数。这意味着任何结果不带有IO类型构造子的函数都保证不使用 IO monad。其他monad,如List,Maybe允许把值传出monad。因此可以写内部使用了这些monad但返回非 monadic 值的函数。
关于“单向”monad的一个非常棒的特性是:在它的monadic操作中支持“副作用”且阻止这些副作用破坏程序中的非monadic部分的函数式特性。(译注:IO monad保持了 Haskell纯函数式语言的一致性,把副作用隔离到一个可控的范围内.)
考虑一个从用户读取一个字符的简单情况。我们不能简单地写一个函数 readChar :: Char,因为依赖于用户的输入,每次调用都需要返回不同的字符。Haskell作为纯函数式语言的一个基本属性就是所有的函数用同样的参数调用两次返回同样的值。但在 IO monad 中用 getChar :: IO Char 来做 I/O 操作却没问题,因为它只能在一个单向的monad中串行的使用。一个函数一旦使用了带IO类型的函数,便无法在其函数类型中去掉 IO 类型,这样IO类型构造子便可以标示出所有执行了 I/O 操作的函数。进一步说,这些函数也只能在一个 IO monad中有用。这样,一个单向的monad有效的创建了一个隔离环境,在这个环境中纯函数式语言的规则可以不那么严格。函数式的计算可以放进这个环境,但是危险的副作用和违反引用透明性的函数则无法逃离这个环境。
定义monad的另一种通常模式是用函数来表示monadic的值。这样当需要一个monadic的计算的值时,返回的monad是一次“执行”来给出结果。
Zero and Plus
除了上面说的monad三个规则之外,一些monad还遵守附加规则。有一个特殊值 mzero 和一个操作符 mplus 的monad还需遵守四个附加的规则:

mzero >>= f == mzero
m >>= (\x -> mzero) == mzero
mzero `mplus` m == m
m `mplus` mzero == m
把mzero, mplus, >>= 与普通数学中的 0, +, * 联系起来就好记了。

有zero和plus的monad可以用 Haskell中的 MonadPlus类来声明:


class (Monad m) => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a


继续用 Maybe monad作例子,Maybe是一个MonadPlus的 instance:
instance MonadPlus Maybe where
    mzero             = Nothing
    Nothing `mplus` x = x
    x `mplus` _       = x


这确定了 Nothing 作为 zero, 把两个 Maybe的值放在一起相加则返回其中第一个不为 Nothing的值.当两个输入值都是Nothing的时候, mplus 的结果也是 Nothing.
List monad 也有zero 和 plus, mzero就是空表, mplus就是 ++ 操作符.
mplus操作符用来把分离的计算组合成一个单独的monadic的值。在我们的克隆羊的例子中,我们可以用Maybe的 mplus来定义一个函数, parent s = (mother s) `mplus` (fater s),如果s有一个父亲或母亲的话则返回一个,如果根本没有父母的话则返回Nothing。如果一只羊父母都有,那么返回父亲或母亲要看Maybe monad中mplus的具体定义。
Summary
Monad class的Instance 需要满足所谓的“monad规则”,这些规则描述了monad对代数特性。有三个规则规定了“return”函数是一个左单位元,并且是一个右单位元,“bind”操作具有结合性。如不满足这三个规则,在用do形式时函数行为将不正常并产生微妙的问题。
除了 return 和 >>= 函数,Monad class定义了另外的函数 fail。 fail 函数并不是monad所必需的,但在实际中它经常很有用,而且haskell的 do 形式用到了它,因此它被包含着Monad Class中。
有些monad遵守三个基本规则之外的规则。这样的monad中有一类很重要,他们包含 zero元素和 plus操作符。Haskell为这类monad提供了 MonadPlus class,里面定义了 mzero值和mplus操作。


0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics