Do表示法用于编写一般形式的表达式
ex :: Monad m => m t
let ex = do
{ x <- foo -- foo :: Monad m => m a, x :: a
; y <- bar x -- bar x :: Monad m => m b, y :: b
; z <- baz x y -- baz x y :: Monad m => m c, z :: c
; quux x y z -- quux x y z :: Monad m => m t
}
请注意,所有的m
s 都是相同的,并且a
, b
, c
, ... 可以不同,尽管t
最后一个do子表达式的类型和整个do表达式的类型是相同的。
do表示法变量被认为是由<-
构造“绑定”的。它们在引入时进入范围(在 的左侧<-
),并保留在所有后续do子表达式的范围内。
一个可用于任何 monad 的内置 monadic 表达式是return :: Monad m => a -> m a
. 因此x <- return v
绑定 x
到v
,以便x
在后续子表达式中可用,并且将具有 的值v
。
所有do变量都被限制在该do
块中,不能在它之外使用。每个变量的作用域是同do
一块中的所有代码,位于变量绑定的下方/之后。
这也意味着<-
's 是一个非递归绑定,因为变量不能在其右侧和左侧一样:在这种情况下,它将是两个具有相同名称的不同变量,并且变量位于必须在高于该点的某个地方确立权利。
这里有一些通用模式:
do { _ <- p ; _ <- q ; r } === do { p ; q ; r }
do { x <- p ; return x } === do { p } === p
do { x <- return v ; foo x } === do { foo v } === foo v
do { p ; q ; r } === do { p ; do { q ; r } }
=== do { do { p ; q } ; r }
do { x <- p ; === do { x <- p ;
y <- q x ; z <- do { y <- q x ;
return (foo x y) } return (foo x y) } ;
return z }
所有的Monad m => m a
表达式都是这样,表达式,因此特别是可以是一个if - then - else
表达式,其结果分支和替代分支都是相同的单子类型(这通常会让初学者感到困惑):
do { x <- p ;
y <- if (pred x) then (foo x) else (bar x) ;
return (baz x y) }
更新: monad 的要点之一是将效果与纯计算完全分离。一旦进入单子,你就不能“出去”。一元计算可以使用纯计算,反之则不行。