从风格上讲,这非常好。在现实世界中,我预计这种符号的可能性为 60%,而不是您给出的那个:
C x c >>= f = C (value $ f x) (c + 1)
但这太小了,几乎不值得一提。
更严肃地说,不是文体而是语义:这不是单子。事实上,它违反了所有三个单子定律。
(1) return x >>= f = f x
(2) m >>= return = m
(3) m >>= (f >=> g) = (m >>= f) >>= g
(其中(>=>)被定义为f >=> g = \x -> f x >>= g。如果(>>=)被认为是一个“应用”运算符,那么(>=>)就是对应的组合运算符。我喜欢用这个运算符陈述第三定律,因为它带来了出第三定律的含义:结合性。)
通过这些计算:
(1):
return 0 >>= return
= C 0 0 >>= return
= C (value $ return 0) 1
= C 0 1
Not equal to return 0 = C 0 0
(2):
C 0 0 >>= return
= C (value $ return 0) 1
= C 0 1
Not equal to C 0 0
(3)
C 0 0 >>= (return >=> return)
= C (value $ (return >=> return) 0) 1
= C (value $ return 0 >>= return) 1
= C (value $ C 0 1) 1
= C 0 1
Is not equal to:
(C 0 0 >>= return) >>= return
= C (value $ return 0) 1 >>= return
= C 0 1 >>= return
= C (value $ return 0) 2
= C 0 2
这不仅仅是您的实现中的错误——没有“计算绑定数量”的 monad。它必须违反法律 (1) 和 (2)。但是,您违反法律 (3) 的事实是实施错误。
问题在于(>>=) 的定义中的f 可能会返回一个包含多个绑定的操作,而您忽略了这一点。您需要添加左右参数的绑定数量:
C x c >>= f = C y (c+c'+1)
where
C y c' = f x
这将正确计算绑定的数量,并满足第三定律,即结合定律。它不会满足其他两个。然而,如果你从这个定义中去掉+1,那么你确实得到一个真正的monad,它相当于Writer monad 在+ monoid 之上。这基本上将所有子计算的结果加在一起。您可以使用它来计算 somethings 的数量,而不是绑定 - 绑定太特殊而无法计算。但是,例如:
tick :: C ()
tick = C () 1
然后C 将统计计算中发生的ticks 的数量。
实际上,您可以将Int 替换为任何类型,将(+) 替换为任何关联 运算符并获得一个monad。这就是Writer monad 的一般含义。如果运算符不是关联的,那么这将不符合第三定律(你明白为什么吗?)。