寻求对monad实施的建设性批评

abe*_*sto 33 monads haskell idiomatic

我正在学习monad,这是我的第一个工作(除了琐碎的monad).随意批评其中的一切无情.我对"更惯用"和"更优雅"的回应特别感兴趣.

该monad计算执行的绑定数.

data C a = C {value :: a, count :: Int} deriving (Show)

instance Monad C where
    (>>=) (C x c) f = C (value $ f x) (c + 1)
    return x = C x 0

add :: (Num a) => a -> a -> C a
add x y = return $ x + y

-- Simpler way to do this? foldM is obviously something different.
mysum [x] = return x
mysum (x:xs) = mysum xs >>= add x
Run Code Online (Sandbox Code Playgroud)

luq*_*qui 94

在风格上这是非常好的.在现实世界中,我希望这种符号有60%的可能性而不是你给出的那种:

C x c >>= f = C (value $ f x) (c + 1)
Run Code Online (Sandbox Code Playgroud)

但这太小了,几乎不值得一提.

更严肃的说,不是风格而是语义:这不是一个单子.事实上,它违反了所有三个monad法律.

(1) return x >>= f  =  f x
(2) m >>= return    = m
(3) m >>= (f >=> g) = (m >>= f) >>= g
Run Code Online (Sandbox Code Playgroud)

(如果(>=>)被定义为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
Run Code Online (Sandbox Code Playgroud)

(2):

C 0 0 >>= return
  = C (value $ return 0) 1
  = C 0 1
Not equal to C 0 0
Run Code Online (Sandbox Code Playgroud)

(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
Run Code Online (Sandbox Code Playgroud)

这不仅仅是您实现中的错误 - 没有"计算绑定数量"的monad.它必须违反法律(1)和(2).但是,您的违反法律(3)的事实是一个实施错误.

麻烦的是,f(>>=)might 的定义中返回一个具有多个绑定的动作,而你却忽略了这一点.您需要添加左右参数的绑定数量:

C x c >>= f = C y (c+c'+1)
   where
   C y c' = f x
Run Code Online (Sandbox Code Playgroud)

这将正确计算绑定数量,并将满足第三定律,即相关性定律.它不会满足其他两个.但是,如果你+1从这个定义中删除,那么你获得一个真正的monad,这相当于monoid 上的Writermonad +.这基本上将所有子计算的结果加在一起.您可以使用它来计算某些事物的数量,不是绑定 - 绑定太特殊而无法计算.但是,例如:

tick :: C ()
tick = C () 1
Run Code Online (Sandbox Code Playgroud)

然后Ctick计算计算中发生的s 数.

实际上,您可以Int使用任何类型和(+)任何关联运算符替换并获取monad.这就是Writermonad的一般情况.如果操作员不是关联的,那么这将违反第三定律(你能明白为什么吗?).

  • 另外:美丽深入的解释.谢谢. (10认同)