理解应用函子的关键是找出它们所保留的结构。
常规函子保留基本的范畴结构:它们映射范畴之间的对象和态射,并保持范畴的定律(结合律和单位元)。但是,一个范畴可能具有更多的结构。例如,它可以允许定义类似于态射但需要多个参数的映射。这些映射由柯里化来定义:例如,两个参数的函数被定义为返回另一个函数的一个参数函数。如果您可以定义表示函数类型的对象,则可以实现此目标。通常,该对象称为指数(在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卷积。但这更难解释。