【问题标题】:Seeking constructive criticism on monad implementation寻求对 monad 实施的建设性批评
【发布时间】:2011-06-13 12:13:54
【问题描述】:

我正在学习 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

【问题讨论】:

    标签: haskell monads idioms


    【解决方案1】:

    从风格上讲,这非常好。在现实世界中,我预计这种符号的可能性为 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 的一般含义。如果运算符不是关联的,那么这将不符合第三定律(你明白为什么吗?)。

    【讨论】:

    • 这不是我所期望的,但肯定是我需要的。关于如果运算符不是关联的,为什么会破坏第三定律:因为第三定律是绑定的关联性。如果 bind “做”某事不具有关联性(我不能给出“做”的确切定义),那么它本身也不能是关联的。对吗?
    • 还有:漂亮的深入解释。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 2010-09-07
    • 2014-02-28
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多