箭头
通过范畴进行广义化,因此通过Category
类型类完成。
class Category f where
(.) :: f a b -> f b c -> f a c
id :: f a a
Arrow
类型类定义具有超类
Category
。在 Haskell 中,范畴(category)概括了函数(可以组合它们但无法应用它们),因此它们绝对是一种“计算模型”。
Arrow
提供了一个带有附加结构的
Category
以处理元组,因此,虽然
Category
反映了关于 Haskell 函数空间的某些内容,但
Arrow
将其扩展到有关乘积类型的内容。
每个
Monad
都会产生一种称为“Kleisli 范畴”的东西,这种构造将为您提供
ArrowApply
实例。您可以从任何
ArrowApply
构建一个
Monad
,以便完全回到原来的行为,因此在某种深层意义上,
Monad
和
ArrowApply
是相同的事物。
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (return . f)
first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
实际上,每个箭头(
Arrow
)除了
Category
超类之外,都会引出一个
Applicative
(通常是普遍量化以获得正确的种类),我相信适当的
Category
和
Applicative
的组合足以重构你的
Arrow
。
因此,这些结构是深度相关的。
警告:以下是犹豫不决的评论。 Functor
/ Applicative
/ Monad
思考方式和 Category
/ Arrow
思考方式之间的一个主要区别是,在 对象级别 (Haskell 中的类型)上,Functor
及其类似物是泛化,而 Category
/ Arrow
是 态射 概念的泛化(在 Haskell 中是函数)。 我认为,在广义 态射 的层面进行思考涉及比在广义 对象 的层面思考更高层次的抽象。 有时候这是一件好事,但有时候则不是。 另一方面,尽管 Arrow
具有范畴基础,而且数学界中没有人认为 Applicative
是有趣的,但我了解到,通常比 Arrow
更容易理解 Applicative
。
基本上可以将“Category < Arrow < ArrowApply”和“Functor < Applicative < Monad”视为“Category ~ Functor”,“Arrow ~ Applicative”和“ArrowApply ~ Monad”。
更具体如下:
至于用于模拟计算的其他结构:人们可以经常颠倒范畴构造中的“箭头”(这里只是指态射),以获得“对偶”或“共构造”。 因此,如果一个单子(monad)被定义为
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
(好的,我知道这并不是Haskell如何定义事物的方式,但ma >> = f = join $ fmap f ma
和join x = x >>= id
,因此它同样可以)那么共函子是
class Functor m => Comonad m where
extract :: m a -> a
duplicate :: m a -> m (m a)
原来这种东西也很常见。事实证明,Comonad
是元胞自动机的基本底层结构。为了完备起见,我应该指出Edward Kmett的Control.Comonad
将duplicate
放在“可扩展函子”类中,在函子和Comonad
之间,因为您还可以定义
extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
extend f = fmap f . duplicate
--this is enough
duplicate = extend id
事实证明,所有的Monad
也都是“可扩展”的。
monadDuplicate :: Monad m => m a -> m (m a)
monadDuplicate = return
所有的 Comonads
都是“可合并的”
comonadJoin :: Comonad m => m (m a) -> m a
comonadJoin = extract
所以这些结构非常靠近彼此。