回忆一下
Applicative
课程:
class Functor f => Applicative f where
pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(<*>) :: f (a -> b) -> f a -> f b
liftA2 h x y = fmap h x <*> y
(<*>) = liftA2 id
虽然目前还不清楚如何用(数学)范畴论来表达这个类,但当定义以下函数时就会变得清晰:
liftZ2 :: Applicative f => (f a, f b) -> f (a, b)
liftZ2 (x, y) = liftA2 (,) x y
在这里,
(,)
应该可以被识别为分类产品。用余积替换积并反转所有箭头给出以下类别:
class Functor f => Coapplicative f where
copure :: f a -> a
coliftZ2 :: f (Either a b) -> Either (f a) (f b)
一些实例:
import Data.Functor.Identity
import qualified Data.List.NonEmpty as NonEmpty
import Data.Either
instance Coapplicative Identity where
copure (Identity x) = x
coliftZ2 (Identity (Left x)) = Left (Identity x)
coliftZ2 (Identity (Right y)) = Right (Identity y)
instance Coapplicative NonEmpty.NonEmpty where
copure (x NonEmpty.:| _) = x
coliftZ2 (Left x NonEmpty.:| xs) = Left (x NonEmpty.:| lefts xs)
coliftZ2 (Right y NonEmpty.:| ys) = Right (y NonEmpty.:| rights ys)
instance Coapplicative ((,) e) where
copure (_, x) = x
coliftZ2 (e, Left x) = Left (e, x)
coliftZ2 (e, Right y) = Right (e, y)
instance Monoid m => Coapplicative ((->) m) where
copure f = f mempty
coliftZ2 f = case copure f of
Left x -> Left (fromLeft x . f)
Right y -> Right (fromRight y . f)
我有一种强烈的直觉,
Coapplicative
是Comonad
的超类,但我不知道如何证明它。另外,是否有一个 Coapplicative
实例不是 Comonad
?
这不是一个完整的答案,但我至少可以证明你的
Coapplicative NonEmpty
实例不能单独从 Comonad
方法派生;也就是说,如果有一些参数化的实现
coliftZ2 :: (Comonad w) => w (Either a b) -> Either (w a) (w b)
那么它不会为
NonEmpty
生成您的实例。这是因为 Comonad
本身不允许我们以任何方式更改列表的长度——事实上,所有固定长度向量都是共元,但 NonEmpty
的 coliftZ2
实例会改变长度。因此,如果 NonEmpty
要成为 Coapplicative
,它必须以其他方式实现。
我需要去睡觉了,但我想说探索 comonad 是值得的
data Two a = Two a a
deriving (Functor)
instance Comonad Two where
extract (Two x _) = x
duplicate (Two x y) = Two (Two x y) (Two y x)
并考虑其
coliftZ2
的实施必须是什么。你还没有给出 Coapplicative
的任何定律,但如果你这样做了,我怀疑这将是一个很好的反例,因为我发现 coliftZ2
没有实现令人满意的对称性。这两个方程都相当牵强,但它们似乎表明了运算的非常不同的含义。
coliftZ2 (Two (Left x) (Left y)) = Left (Two x y)
coliftZ2 (Two (Left x) (Right y)) = Left (Two x x)