从范畴论的角度来看,什么是应用函子定义?

31

我能够将范畴论中Functor的定义映射到Haskell定义中,具体方法如下:由于Hask中的对象是类型,所以函子F

  • 通过将每个类型a映射到新类型F a,粗略地说,将"F"添加到其前面。
  • 使用fmap :: (a -> b) -> (f a -> f b),将Hask的每个态射a -> b映射到新的态射F a -> F b

到这里为止都很好。现在我来到了Applicative,但在教材中找不到这种概念的提及。看了看它给Functor新增的内容ap :: f (a -> b) -> f a -> f b,我试图自己想出一个定义。

首先,我注意到由于(->)也是一种类型,因此Hask中的态射也是它的对象。考虑到这一点,我建议应用函子是一种函子,也可以将源类别的“箭头”对象映射到目标类别的态射中。

这个直觉正确吗?您能否提供更加正式和严谨的定义?


2
http://cstheory.stackexchange.com/questions/12412/explaining-applicative-functor-in-categorical-terms-monoidal-functors - n. m.
2个回答

47
理解应用函子的关键是找出它们所保留的结构。
常规函子保留基本的范畴结构:它们映射范畴之间的对象和态射,并保持范畴的定律(结合律和单位元)。但是,一个范畴可能具有更多的结构。例如,它可以允许定义类似于态射但需要多个参数的映射。这些映射由柯里化来定义:例如,两个参数的函数被定义为返回另一个函数的一个参数函数。如果您可以定义表示函数类型的对象,则可以实现此目标。通常,该对象称为指数(在Haskell中,它只是类型b->c)。然后我们可以从一个对象到指数具有态射,并将其称为二元态射。
Haskell中应用函子的传统定义基于将多个参数的函数进行映射的想法。但是,存在一种沿不同边界拆分多参数函数的等效定义。您可以将这样的函数视为将(在Haskell中为一对)映射到另一种类型(在此处为c)。
a -> (b -> c)  ~  (a, b) -> c

这使我们可以将applicative functors视为保留乘积的functors。但是,乘积只是所谓的单调结构的一个例子。

一般来说,单调范畴是指配备有张量积和单位对象的范畴。在Haskell中,例如笛卡尔积(a pair)和单位类型()。然而,请注意,单调律(结合律和单位律)仅在同构意义下有效。例如:

(a, ())  ~  a

一个应用函子可以被定义为保留单调结构的函子。特别是,它应该保留单位元和乘积。无论我们在应用函子之前还是之后进行“乘法”,结果都应该是同构的。

然而,我们并不真正需要一个完整的单调函子。我们只需要两个态射(而不是同构)——一个用于乘法,一个用于单位元。这样一个仅部分保留单调结构的函子被称为松散单调函子。因此,另一种定义是:

class Functor f => Monoidal f where
  unit :: f ()
  (**) :: f a -> f b -> f (a, b)

很容易证明Monoidal等同于Applicative。例如,我们可以从unit得到pure,反之亦然:

pure x = fmap (const x) unit
unit = pure ()

应用定律仅源于保留幺半律(结合律和单位元律)。

在范畴论中,保留单拓扑结构与张量强度相关,因此适用函子也称为强松散单拓扑函子。但是,在Hask中,每个函子对于乘积都有规范强度,所以这一属性对定义没有任何添加。

如果您熟悉将单子定义为自函子范畴中的单拓扑,您可能会想知道应用程序类似地是自函子范畴中的单子,其中张量积为Day卷积。但这更难解释。


1
这个“strength”是什么东西? - dfeuer
是的,如果您能在此处添加有关“strength”的含义的一些说明,那么将会更加清晰;特别是左边about的答案链接到https://en.wikipedia.org/wiki/Monoidal_functor,该页面似乎定义了“强莫诺函子”为“假定某些约束条件的莫诺函子”,而“松散莫诺函子”则意味着“没有额外的假设”,因此在这种术语中,“强松散莫诺函子”似乎没有意义。 - Ben
8
我个人更喜欢将applicative functor称为“闭合函子”,而不是单纯的单调函子。它们是单纯单调的这一事实是巧合,主要是由于指数的保留所迫使的,这就是为什么“单调”编码方式感觉如此混乱的原因,而我们使用的(<*>) :: f (a -> b) -> f a -> f b组合器恰好就是指数映射!从另一个角度来看,Applicative可以被视为相对于(协变)Day卷积的幺半对象。这种观点的好处在于阐明了找到逆变形式的Applicative的路径。 - Edward Kmett
4
当我将“(**)”在脑海中转换为“(f a, f b) -> f (a, b)”时,这个概念对我来说更加合理。我不明白为什么你一开始没有用这种方式来表达它。 - arrowd
实际上,还有一种可能更“深入”的变体是 ((a,b)->c) -> (f a,f b)->f c,这反映了函子将一个范畴的单调结构转换为另一个范畴的单调结构(尽管这两个范畴在此处都是 Hask 范畴)。 - leftaroundabout
(**) is Haskell's liftA2 which is a second possible minimal definition of Applicative together with pure - oquechy

15

你是对的,ApplicativeFunctor或者Monad更加难以直接理解。但本质上,它是幺半范畴的类:

class Functor f => Monoidal f where
  pureUnit :: f ()
  fzip :: f a -> f b -> f (a,b)

从那里,您可以在Hask中定义 -

pure x = fmap (const x) pureUnit

fs <*> xs = fmap (uncurry ($)) $ fzip fs xs

参见此答案,了解ApplicativeMonoidal确实是等价的完整证明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接