Haskell中非常规Monad的>>=实现下,如何理解do符号里的<-?
关于Haskell Monad的非常规bind实现与do符号<-的理解
1. 存在满足Monad定律的非常规bind实现
当然存在。Monad的定义仅要求满足三条核心定律(左单位、右单位、结合律),bind的具体行为只要符合这三条即可,并不局限于“取出容器内的值应用函数”这种常见模式。举两个具体实现案例:
例子1:日志顺序反转的Writer Monad
常规Writer Monad的bind会先追加当前计算的日志,再追加后续函数产生的日志;我们可以实现一个日志顺序反转的版本,完全满足所有Monad定律:
import Data.Monoid newtype Writer' w a = Writer' (a, w) deriving (Show) runWriter' :: Writer' w a -> (a, w) runWriter' (Writer' x) = x instance Monoid w => Monad (Writer' w) where return x = Writer' (x, mempty) -- 非常规点:日志顺序是后续函数的日志在前,当前计算的日志在后 Writer' (a, w1) >>= f = let Writer' (b, w2) = f a in Writer' (b, w2 <> w1)
验证三条定律:
- 左单位:
return a >>= f等价于f a,满足; - 右单位:
m >>= return等价于m,满足; - 结合律:
(m >>= f) >>= g与m >>= (\x -> f x >>= g)结果一致(依赖Monoid的结合性),满足。
例子2:累加绑定次数的Count Monad
这个Monad的bind会累加每次绑定的次数,同时传递包裹的值:
newtype Count a = Count (Int, a) deriving (Show) instance Monad Count where return x = Count (0, x) -- 非常规点:每次绑定都会将计数+1并累加 Count (n, a) >>= f = let Count (m, b) = f a in Count (n + m + 1, b)
同样满足所有Monad定律:左单位、右单位、结合律均成立。
2. 非常规bind下do符号<-的理解
do符号的语法糖定义是固定的:
do { v <- y; rest } = y >>= (\v -> rest)
因此<-的含义完全由对应Monad的>>=实现决定,不能僵化套用“绑定容器内的值到变量”的常规语义,要从>>=的行为出发理解:
- 它本质是Monadic计算的链式连接器:左侧的
y是一个Monadic计算,<-将这个计算的“结果”(这里的“结果”由>>=定义)传递给后续代码中的变量v; - 变量
v的具体来源、以及y的Monadic上下文如何影响后续计算,完全取决于该Monad的>>=逻辑:- 在上述
Writer'例子中,v依然是y中包裹的实际值,但y的日志会和后续计算的日志反转拼接; - 如果有一个更极端的Monad(比如
IgnoreValue,bind时忽略输入值,固定传递()给后续函数),那么v <- y中的v永远是(),和y本身的内容无关;
- 在上述
- 核心思路:不要把<-当成“变量赋值”,而是当成“Monadic计算之间的协作约定”——具体约定由当前Monad的
>>=定义。
内容的提问来源于stack exchange,提问作者feipiao
相关产品推荐
相关产品推荐

