完全没有限制!当我开始学习类型构造函数的类别理论基础时,这一点也使我感到困惑。我们将解决这个问题。但是首先,让我澄清一些混乱。这两个引号:
这样的函子只能将使用类型构造函数构造的类别作为目标类别
和
可能会想到具有任何类别作为函子目标的函子,例如所有Haskell类型的类别
说明您误解了函子是什么(或者至少是您误用了术语)。
函子不构成类别。函子是类别之间的映射。函数将源类别中的对象和形态(类型和函数)带到目标类别中的对象和形态。
请注意,这意味着函子实际上是一对映射:对象F_obj上的映射和态射F_morph上的映射。在Haskell中,函子的对象部分F_obj是类型构造函数的名称(例如List
),而形态学部分是函数fmap
(取决于Haskell编译器来整理fmap
我们在任何给定表达式中引用的内容)。因此,我们不能说这List
是一个函子。只的组合List
和fmap
是一个算符。尽管如此,人们还是滥用符号。程序员List
将函子称为函子,而类别理论家则使用相同的符号来引用函子的两个部分。
此外,在编程中,几乎所有的函子都是内函子,即源类别和目标类别是相同的-我们语言中所有类型的类别。我们将此类别称为Type。一个endofunctor ˚F上型类型映射Ť到另一种类型的FT和功能笔- >取值给另一个函数FT - > FS。当然,此映射必须遵守函子定律。
使用List
为例:我们有一个类型构造List : Type -> Type
和功能fmap: (a -> b) -> (List a -> List b)
,共同构成一个仿函数。Ť
最后一点需要澄清。编写List int
不会创建新的整数列表类型。这种类型已经存在。这是我们类别Type中的一个对象。List Int
只是引用它的一种方式。
现在,您想知道为什么函子无法将类型映射到例如Int
或String
。但是,可以!只需使用身份函子即可。对于任何类别C,身份仿函数都将每个对象映射到自身,并将态射映射到自身。验证此映射是否满足函子定律很简单。在Haskell中,这将是一个id : * -> *
将每个类型映射到自身的类型构造函数。例如,id int
计算为int
。
而且,甚至可以创建恒定的函子,将所有类型映射到一个类型。例如,functor ToInt : * -> *
(ToInt a = int
对于所有类型为)a
,并将所有态射映射到整数标识函数: fmap f = \x -> x