假设我有一个Pair
类型:
data Pair a = Pair a a
为它编写monad实例的正确方法是什么?我的想法大致是这样的:instance Semigroup a => Semigroup (Pair a) where
Pair a1 a2 <> Pair b1 b2 = Pair (a1 <> b1)(a2 <> b2)
instance Monad Pair where
return = pure
(Pair a b) >>= f = f a <> f b
这是正确的吗?如果是这样,那么在哪里指定b
中的Pair b
-type是一个半组? 最佳答案
实际上,Pair
的唯一正确的monad实例如下。
instance Monad Pair where
m >>= f = joinPair (f <$> m)
joinPair :: Pair (Pair a) -> Pair a
joinPair (Pair (Pair x _) (Pair _ y)) = Pair x y
之所以是正确的monad实例,是因为Pair
是representable functor。instance Representable Pair where
type Rep Pair = Bool
index (Pair x _) False = x
index (Pair _ y) True = y
tabulate f = Pair (f False) (f True)
事实证明,对于每个可表示的仿函数(>>=)
都等效于以下 bindRep
函数。bindRep :: Representable f => f a -> (a -> f b) -> f b
bindRep m f = tabulate (\a -> index (f (index m a)) a)
如果我们将bindRep
函数专用于Pair
,我们将得到以下结果。bindRep :: Pair a -> (a -> Pair b) -> Pair b
bindRep (Pair x y) f = tabulate (\a -> index (f (index (Pair x y) a)) a)
= Pair (index (f x) False) (index (f y) True)
-- |_________________| |________________|
-- | |
-- (1st elem of f x) (2nd elem of f y)
以下Adelbert Chang的博客文章对此进行了更好的解释。 Reasoning with representable functors。这是证明独特性的另一种方法。考虑左右身份单子(monad)实例法则。
return a >>= k = k a -- left identity law
m >>= return = m -- right identity law
现在,对于Pair
数据类型return x = Pair x x
。因此,我们可以专门研究这些法律。Pair a a >>= k = k a -- left identity law
m >>= \x -> Pair x x = m -- right identity law
那么,为了满足这两个定律,>>=
的定义应该是什么?Pair x y >>= f = Pair (oneOf [x1, x2, y1, y2]) (oneOf [x1, x2, y1, y2])
where Pair x1 y1 = f x
Pair x2 y2 = f y
oneOf
函数不确定地返回列表的元素之一。现在,如果我们的
>>=
函数满足左身份定律,那么当x = y
时,则x1 = x2
和y1 = y2
的结果必须是Pair (oneOf [x1, x2]) (oneOf [y1, y2])
。同样,如果我们的
>>=
函数要满足正确的身份定律,则x1 = y1 = x
和x2 = y2 = y
的结果必须是Pair (oneOf [x1, y1]) (oneOf [x2, y2])
。因此,如果您想同时满足这两个定律,那么唯一有效的结果就是
Pair x1 y2
。关于haskell - 如何为两个参数都具有相同类型的对编写monad实例?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58753545/