如何在GHC 7.10及更高版本中为自定义类型实现Monad
为WhoCares类型实现Monad实例
《Haskell Programming from First Principles》中提到:
若使用GHC 7.10或更高版本,你会看到Monad的定义中包含Applicative约束,这是合理的:
class Applicative m => Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a
我已经实现了WhoCares类型的Functor和Applicative实例:
data WhoCares a = ItDoesnt | Matter a | WhatThisIsCalled deriving (Eq, Show) instance Functor WhoCares where fmap _ ItDoesnt = ItDoesnt fmap _ WhatThisIsCalled = WhatThisIsCalled fmap f (Matter a) = Matter (f a) instance Applicative WhoCares where pure = Matter Matter f <*> Matter a = Matter (f a)
但我不清楚如何为该类型实现>>=和>>方法,目前尝试的代码存在错误:
instance Monad (WhoCares a) where (>>=) :: Matter a -> (a -> Matter b) -> Matter b (>>) :: Matter a -> Matter b -> Matter b return :: a -> Matter a return = pure
期望的用法与输出
我希望实现后能支持以下测试代码,并得到对应输出:
测试代码
half x = if even x then Matter (x `div` 2) else ItDoesnt incVal :: (Ord a, Num a) => a -> WhoCares a incVal x | x + 1 <= 10 = return (x + 1) | otherwise = ItDoesnt decVal :: (Ord a, Num a) => a -> WhoCares a decVal x | x - 1 >= 0 = return (x - 1) | otherwise = ItDoesnt main = do print (Matter 7 >>= incVal >>= incVal >>= incVal) print (Matter 7 >>= incVal >>= incVal >>= incVal >>= incVal) print (Matter 7 >>= incVal >>= incVal >>= incVal >>= incVal >>= decVal >>= decVal) print (Matter 2 >>= decVal >>= decVal >>= decVal) print(Matter 20 >>= half >>= half)
预期输出
Matter 10 ItDoesnt ItDoesnt ItDoesnt Matter 5
正确的Monad实例实现
你的代码存在两个核心问题:
- 实例声明错误:
instance Monad (WhoCares a)是具体类型的写法,Monad实例需要针对类型构造器声明,即instance Monad WhoCares - 未处理所有构造器:
>>=和>>需要覆盖WhoCares的所有三种情况(ItDoesnt、Matter、WhatThisIsCalled)
完整的正确实现如下:
instance Monad WhoCares where return = pure -- 复用Applicative的pure,语义一致 -- 绑定操作>>=的实现 ItDoesnt >>= _ = ItDoesnt WhatThisIsCalled >>= _ = WhatThisIsCalled Matter a >>= f = f a -- 序列操作>>的实现(可选,不定义则使用默认实现:x >> y = x >>= \_ -> y) ItDoesnt >> _ = ItDoesnt WhatThisIsCalled >> _ = WhatThisIsCalled Matter _ >> y = y
逻辑说明
- return:直接复用Applicative的
pure,因为Monad的return和Applicative的pure都是将普通值包装到类型构造器中。 - >>=(绑定):
- 若当前值是
ItDoesnt或WhatThisIsCalled,直接返回自身,不执行后续函数,符合“失败即终止”的语义 - 若当前值是
Matter a,则将a传入函数f,返回f a的结果,这是Monad绑定的核心逻辑
- 若当前值是
- >>(序列):
- 非
Matter构造器直接返回自身 Matter构造器忽略自身值,返回第二个参数,和默认实现逻辑一致
- 非
现在运行测试代码,就能得到预期的输出结果。
内容的提问来源于stack exchange,提问作者Ahmad Ismail
相关产品推荐
相关产品推荐

