在页面http://en.wikibooks.org/wiki/Haskell/do_Notation上,有一种非常方便的方法可以将do语法转换为功能形式(我的意思是,使用>> =)。它适用于很多情况,直到我遇到一段涉及函数monad的代码(( - >)r)
代码是
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
这相当于定义
addStuff = \x -> x*2+(x+10)
现在,如果我使用方便的方式重写do部分,我得到
addStuff = (*2) >>= \a ->
(+10) >>= \b ->
a + b
给出了编译错误。我知道a,b是Int(或其他类型的Num),所以最后一个函数(\ b - &gt; a + b)的类型为Int - &gt; Int,而不是Int - &gt; Int - &gt;中间体
但这是否意味着并不总是从do转换为&gt;&gt; =?有没有解决这个问题?或者我只是错误地使用规则?
答案 0 :(得分:3)
制作最后一个monadic的问题:
addStuff = (*2) >>= \a ->
(+10) >>= \b ->
return (a + b)
答案 1 :(得分:1)
(你已经回答了)正确的表达式必须在最后一行使用return
:
addStuff = (*2) >>= \a ->
(+10) >>= \b ->
return (a + b)
(详细说明,澄清),即return
是monad定义的一部分,而不是do
符号。
替换((->) r)
monad的实际定义,它相当于
addStuff x
= ((\a -> (\b -> return (a + b)) =<< (+10) ) =<< (*2) ) x
= (\a -> (\b -> return (a + b)) =<< (+10) ) (x*2) x
= ( (\b -> return ((x*2) + b)) =<< (+10) ) x
= (\b -> return ((x*2) + b)) (x+10) x
= return ((x*2) + (x+10)) x
= const ((x*2) + (x+10)) x
= (x*2) + (x+10)
正如所料。所以一般来说,对于函数来说,
do { a <- f ; b <- g ; ... ; n <- h ; return r a b ... n }
与
相同\ x -> let a = f x in let b = g x in ... let n = h x in r a b ... n
(除了每个标识符a,b,...,n
不应出现在相应的函数调用中,因为let
绑定是递归的,而do
绑定不是。)
以上do
代码也正是在Control.Monad中定义liftM2
的原因:
> liftM2 (+) (*2) (+10) 100
310
任何liftM_N
的 N
都可以使用liftM
和ap
进行编码:
> (\a b c -> a+b+c) `liftM` (*2) `ap` (+10) `ap` (+1000) $ 100
1410
liftM
是fmap
的monadic等价物,其函数为(.)
,所以
(+) `liftM` (*2) `ap` (+10) $ x
= (+) . (*2) `ap` (+10) $ x
= ((+) . (*2)) x ( (+10) x )
= (x*2) + (x+10)
因为ap f g x = f x (g x)
代表函数(a.k.a。 S -combinator)。