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
的对偶 extract
和 bind
的对偶 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
因此,如果我们查看从 Applicative
到 Monad
的步骤,两者之间的逻辑步骤将是具有 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
请注意,我们不能用 extend
或 duplicate
定义 extract
,我们也不能用 pure
/return
定义 bind
或 join
,所以这似乎是 "logical" 步骤。 apply
在这里几乎无关紧要;它可以定义为 Extract
或 Monad
,只要它们的定律成立:
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
(获取值)对于 Monad
。 Apply
或多或少是一路上的幸福小意外。有趣的是 Hask 中是否有类型具有 Extract
,但没有 Comonad
(或 Extend
但没有 Comonad
,见下文),但我想那些是相当罕见。
请注意 Extract
尚不存在。但是 2010 report 中的 Applicative
也没有。此外,任何既是 Extract
又是 Applicative
实例的类型都会自动成为 Monad
和 Comonad
,因为您可以定义 bind
和 extend
根据 extract
和 pure
:
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
定义 apply
是 class 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?。
我们可以求解 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
的对偶 extract
和 bind
的对偶 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
因此,如果我们查看从 Applicative
到 Monad
的步骤,两者之间的逻辑步骤将是具有 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
请注意,我们不能用 extend
或 duplicate
定义 extract
,我们也不能用 pure
/return
定义 bind
或 join
,所以这似乎是 "logical" 步骤。 apply
在这里几乎无关紧要;它可以定义为 Extract
或 Monad
,只要它们的定律成立:
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
(获取值)对于 Monad
。 Apply
或多或少是一路上的幸福小意外。有趣的是 Hask 中是否有类型具有 Extract
,但没有 Comonad
(或 Extend
但没有 Comonad
,见下文),但我想那些是相当罕见。
请注意 Extract
尚不存在。但是 2010 report 中的 Applicative
也没有。此外,任何既是 Extract
又是 Applicative
实例的类型都会自动成为 Monad
和 Comonad
,因为您可以定义 bind
和 extend
根据 extract
和 pure
:
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
定义 apply
是 class 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?。