The essence of functional programming (2)

竟然要写(2)了。
 
在说monad之前,先要讲讲functor。functor包括了两个变换,一个是同一个结构(集合)里的变换,比如f:a->b,另外一个是从一个结构到另一个结构的变换,比如a -> m a。functor的意思是,把一个结构的变换,提升到另一个结构里实现。整型的list是一个简单的functor,它可以把一个关于整型的变换,比如f x = x + 1 (这是一个累赘的写法,实际上 f = (+1)就可以了),提升上去。具体的行为就跟map类似。它的名字一般叫fmap,也就是说fmap有两个参数,第一个是a->b的映射,第二个是m a,也就是从a结构到 m a这个结构。
 
monad首先是一个functor。它的提升函数名字叫lift。也就是说lift类似于fmap。而每个monad都是从某个类型而来(m a里面的a)。monad至少要有两个函数:return(或者叫unit),>>= (或者叫bind)。return需要一个a类型的参数,返回一个m a类型的结果,unit这个名字表明了它实际上就是把一个a带进去monad里面。而bind是monad里最重要的函数。bind取两个参数,一个是m a,即当前的monad,另一个是a->m b,表示一个函数,这个函数根据某个a类型的输入可以得到一个m b类型的结果。bind的作用,简单来说就是从当前的monad获得的a生成一个新的monad: m b。
 
functional programming里面有一个很好的特性是组合性。两个函数可以很容易组合成一个新的函数,而在imperative的编程语言里,尤其在有全局变量和有各种锁的时候。但是functional的组合有个问题就是不容易实现imperative里面的控制逻辑,尤其在有laziness的时候。这种控制的逻辑有时候是必须的,比如说有effect的时候,一个例子是需要在console打印一些东西,打印的顺序是需要保留的。monad就是提供了一种机制来做这个事情。前面说了,bind首先会对一个m a求值,然后把结果交给第二个参数,然后得到一个m b。这里m a和m b都可以认为是一个计算,或者说,只有当求值的时候才真正执行。而bind的定义可以保证第一个计算会被先执行。这就获得sequential的性质了。
 
事实上,在>>=的基础上,可以有一个>>的函数。这个函数的定义就是 m a >> m b = m a >>= _ -> m b。也就是说忽略之前的输入,这就跟imperative的顺序执行基本一样了。需要注意的是,这些运算都是在m a这个结构上执行的。而不是在简单的a上。需要说明的是,前面的并不是真正的haskell code…
 
有了monad我们就可以很容易的引入错误处理了。以Maybe类型为例,它是一个简单的monad,有两种值,Just a表示正常的值a,而Nothing表示错误的值。这样,我们可以定义return a = Just a; Just a >>= f = f a; Nothing >>= f = Nothing。注意第三个式子,实际上把Nothing看作错误值就可以看到错误的传播。考虑有多个>>=组合的情况,如果对应到原有的f (Just a) = xxx; f Nothing = Nothing这样的繁复的写,monad的作法要简洁的多。
 
简单来说,monad就是一个自娱自乐的东西。主要是提供functional里面缺乏的imperative的一些特征。不会haskell基本听不懂,会了haskell基本不用说。完毕。。 
 
This entry was posted in Uncategorized. Bookmark the permalink.