首先,语法。空格是应用程序,语义上:
f x = f $ x -- "call" f with an argument x
所以你的表达其实是
numericBinop op params = ((mapM unpackNum) params) >>= return . Number . (foldl1 op)
接下来,运算符由非字母数字字符构建,没有任何空格。这里有. 和>>=。在 GHCi 上运行 :i (.) 和 :i (>>=) 会发现它们的 fixity 规范是 infixl 9 . 和 infixr 1 >>=。 9 高于1 所以. 比>>= 强;因此
= ((mapM unpackNum) params) >>= (return . Number . (foldl1 op))
infixl 9 . 表示. 关联到右边,因此,最后是
= ((mapM unpackNum) params) >>= (return . (Number . (foldl1 op)))
(.) 定义为(f . g) x = f (g x),因此(f . (g . h)) x = f ((g . h) x) = f (g (h x)) = (f . g) (h x) = ((f . g) . h) x;通过 eta-reduction 我们有
(f . (g . h)) = ((f . g) . h)
因此(.) 是关联的,因此括号是可选的。从现在开始,我们也将使用“空白”应用程序删除显式括号。因此我们有
numericBinop op params = (mapM unpackNum params) >>=
(\ x -> return (Number (foldl1 op x))) -- '\' is for '/\'ambda
用do写一元序列更容易,上面等价于
= do
{ x <- mapM unpackNum params -- { ; } are optional, IF all 'do'
; return (Number (foldl1 op x))) -- lines are indented at the same level
}
接下来,mapM可以定义为
mapM f [] = return []
mapM f (x:xs) = do { x <- f x ;
xs <- mapM f xs ;
return (x : xs) }
单子定律要求
do { r <- do { x ; === do { x ;
y } ; r <- y ;
foo r foo r
} }
(您可以在我的this recent answer 中找到do 符号的概述);因此,
numericBinop op [a, b, ..., z] =
do {
a <- unpackNum a ;
b <- unpackNum b ;
...........
z <- unpackNum z ;
return (Number (foldl1 op [a, b, ..., z]))
}
(您可能已经注意到我使用了x <- x 绑定——我们可以在<- 的两侧使用相同的变量名,因为单子绑定不是 递归——因此引入了阴影。)
希望现在更清楚了。
但是,我说的是“首先,语法”。所以现在,它的意义。根据相同的单子定律,
numericBinop op [a, b, ..., y, z] =
do {
xs <- do { a <- unpackNum a ;
b <- unpackNum b ;
...........
y <- unpackNum y ;
return [a, b, ..., y] } ;
z <- unpackNum z ;
return (Number (op (foldl1 op xs) z))
}
因此,我们只需要了解两个“计算”c和d的顺序,
do { a <- c ; b <- d ; return (foo a b) }
=
c >>= (\ a ->
d >>= (\ b ->
return (foo a b) ))
对于涉及的特定 monad,由 bind (>>=) 运算符对给定 monad 的实现确定。
Monad 是用于通用功能组合的 EDSL。计算的顺序不仅包括出现在do 序列中的显式表达式,还包括所讨论的特定 monad 特有的隐式效果,这些效果在幕后以原则和一致的方式执行。这就是首先拥有 monad 的全部意义(嗯,至少是要点之一)。
这里涉及的 monad 似乎关心失败的可能性,以及在失败确实发生的情况下的早期救助。
因此,使用do 代码,我们编写了我们打算发生的本质,并且对于我们来说,在幕后自动处理间歇性故障的可能性。
换句话说,如果unpackNum 计算之一失败,那么整个组合计算 将失败,而不尝试任何后续unpackNum 子-计算。但如果它们都成功了,那么组合计算也会成功。