Applicative 之于 monad 就像 X 之于 comonad

Applicative is to monad what X is to comonad

我们可以求解 X 的方程吗?

Applicative is to monad what X is to comonad

仔细想想,我觉得这其实是一道倒退题。有人可能认为 ComonadApply 之于 Comonad 就像 Applicative 之于 Monad,但事实并非如此。但是要看到这一点,让我们使用 PureScript 的类型类层次结构:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Functor f => Apply f where
    apply :: f (a -> b) -> f a -> f b -- (<*>)

class Apply f => Applicative f where
    pure :: a -> f a

class Applicative m => Monad m where
    bind :: m a -> (a -> m b) -> m b  -- (>>=)
 -- join :: m (m a) -> m a
 -- join = flip bind id

如您所见,ComonadApply 只是 (Apply w, Comonad w) => w。但是,Applicative 使用 pure 将值注入函子的能力才是真正的区别。

Comonad 作为分类对偶的定义由 return 的对偶 extractbind 的对偶 extend(或通过 duplicate 的替代定义作为 join 的对偶):

class Functor w => Comonad w where
    extract   :: w a -> a        
    extend    :: (w a -> b) -> w a -> w b
 -- extend f  = fmap f . duplicate k
 -- duplicate :: w a -> w (w a)
 -- duplicate = extend id

因此,如果我们查看从 ApplicativeMonad 的步骤,两者之间的逻辑步骤将是具有 pure 的对偶类型类:

class Apply w => Extract w where
    extract :: w a -> a

class Extract w => Comonad w where
    extend :: (w a -> b) -> w a -> w b

请注意,我们不能用 extendduplicate 定义 extract,我们也不能用 pure/return 定义 bindjoin,所以这似乎是 "logical" 步骤。 apply 在这里几乎无关紧要;它可以定义为 ExtractMonad,只要它们的定律成立:

applyC f = fmap $ extract f   -- Comonad variant; needs only Extract actually (*)
applyM f = bind f . flip fmap -- Monad variant; we need join or bind

所以 Extract(获取值)对于 Comonad 就像 Applicative(获取值)对于 MonadApply 或多或少是一路上的幸福小意外。有趣的是 Hask 中是否有类型具有 Extract,但没有 Comonad(或 Extend 但没有 Comonad,见下文),但我想那些是相当罕见。

请注意 Extract 尚不存在。但是 2010 report 中的 Applicative 也没有。此外,任何既是 Extract 又是 Applicative 实例的类型都会自动成为 MonadComonad,因为您可以定义 bindextend 根据 extractpure:

bindC :: Extract w => w a -> (a -> w b) -> w b
bindC k f = f $ extract k

extendM :: Applicative w => (w a -> b) -> w a -> w b
extendM f k = pure $ f k    

* 能够根据 extract 定义 applyclass Extend w => Comonad w 可能更可行的一个标志,但可以拆分 Monad变成了class (Applicative f, Bind f) => Monad f,因此Comonad变成了(Extend w, Extract w) => Comonad w,所以或多或少是头发分叉了。

在我看来 Apply class 根本不应该出现在图片中。

例如@Zeta 的回答中 apply 的定义似乎不太规范。特别是,它总是丢弃第一个参数的上下文,只使用第二个参数的上下文。

直觉上,似乎 comonad 是关于 "splitting" 上下文而不是组合,所以 "co-applicative" 应该是相同的。

这个问题似乎有更好的答案:Is there a concept of something like co-applicative functors sitting between comonads and functors?