Haskell中的所有类型类都有范畴论类比吗?

8
考虑一个类型类,其成员为* -> *类型。例如:Functor类型类。众所周知,在Haskell中,这个类型类及其数学(即范畴论)对应关系是一致的。推广一下:

问题1: Haskell中每个成员为* -> *类型的类型类是否都对应于某些范畴之间的函数?

现在考虑一个类型类,其成员为*类型。例如,可以想象一个与组(category of Groups)对应的类型类Group(严格来说,Group将是Hask的子范畴,其对象包含所有的Haskell类型)。推广一下:

问题2: Haskell中每个成员为*类型的类型类是否都对应于某些范畴(技术上是:Hask的某个子范畴)?

从这里,我们可以提出下一个通用问题:

问题3: 类型等于或高于* -> * -> *的类型类是否对应于某些范畴论概念?

而实际上,这整个问题可以概括如下:

通用问题:每个Haskell类型类是否都对应于某些范畴论概念?

编辑:至少可以说,由于每个类型类都包含一些Haskell类型集合作为其成员,因此可以将每个类型类视为Hask的某个子范畴(在.下封闭并使用id)。

Foldable对应于范畴论的哪个概念? - Zeta
@DietrichEpp 这适用于 (Foldable f, Functor f) => f。但是话说回来,没有一个 Foldable 不同时也是 Functor(至少我现在想不到)。 - Zeta
1
我认为根据“子类别”的定义,2是正确的,但1和3对我来说似乎相当遥远。如果我编写一个愚蠢的类,其唯一成员是ProxyFlip (Kleisli IO) Bool,那么这很可能没有太多数学意义。 - leftaroundabout
1
@leftaroundabout,但这样的类型类是否仍然可以作为Hask的某些(奇怪的)子范畴而轻松地发挥作用呢? - George
1
@George:不是关于Hask的。而是关于退化类别(实际上是一个幺半范畴)的,它的唯一对象是*,其态射是* -> *类型构造器?是的,我想这也是显然的。但那真的不是很有趣。 - leftaroundabout
显示剩余2条评论
3个回答

15
当足够严谨地解释时,所有这些问题的答案都是“是”,但原因毫无意义。每个范畴C都会限制到一个离散子范畴|C|,其对象与C相同,但只有恒等态射(因此没有有趣的结构)。至少,Haskell类型上的操作可以无聊地解释为对离散类别|*|上的操作。最近的“角色”故事实际上是(但并非如此)试图承认态射很重要,而不仅仅是对象。类型的“命名”角色相当于在|*|中工作,而不是*
(注意,我不喜欢将“Hask”用作“Haskell类型和函数的范畴”的名称:我担心将一个范畴标记为Haskell范畴会导致我们无法看到Haskell编程中其他丰富的范畴结构。这是个陷阱。)
从不同的严谨性方面来看,您可以将任何旧的垃圾作为任何旧的种类的类型类,没有任何有趣的结构(但是具有仍然可以按范畴方式讨论的平凡结构,如果必须的话)。然而,您在库中找到的类往往具有丰富的结构。设计上,* -> *上的类通常是Functor的子类,除了fmap之外,还需要某些自然变换的存在。
对于问题2,当然,*上的类会给出*的子范畴。从范畴中排除对象并不是问题,因为身份和组合存在的范畴要求给定对象存在态射,但不对存在哪些对象提出要求。这个事实很无聊,因为它是无聊的可能性。然而,许多在*上的Haskell类型类引起的类别比作为*的子类别产生的类别更有趣。例如,Monoid类给出了一个范畴,其中对象是Monoid的实例,箭头是单子同态:不仅仅是任何旧函数f从一个Monoid到另一个Monoid,而是保留结构的函数:f mempty = memptyf(mappend x y)= mappend(f x)(f y)
对于问题3,嗯,在那里,到处都隐藏着大量的范畴结构,肯定有大量的范畴结构可用(可能但不一定)在更高级别上。我特别喜欢在索引集族之间的函子。
type (s :: k -> *) :-> (t :: k -> *) = forall x. s x -> t x

class FunctorIx (f :: (i -> *) -> (j -> *)) where
  mapIx :: (s :-> t) -> (f s :-> f t)

ij重合时,询问这样的f何时成为单子是有意义的。通常的范畴定义足够了,尽管我们已经离开了* -> *
信息是这样的:类型类本身并没有引导出有趣的范畴结构;有许多有趣的范畴结构可以通过各种类型类呈现。从*(集合和函数)到* -> *(函子和自然变换)肯定存在有趣的函子。不要被“Hask”的草率言论蒙蔽了,看到了Haskell中范畴结构的丰富性。

6

这里的一个问题是范畴论,也就是通用抽象概念,是一种可以用来讨论数学中几乎任何东西的理论。因此,我们讨论的所有内容都可以用范畴论语言表达,但我们可能不会得出任何有趣的结果。

Haskell中,每个成员类型为* -> *的typeclass是否对应于某个类别之间的函数?

不是。 这个问题包含了类型错误!函数将集合映射到集合,但是范畴不是集合。换句话说,函数是范畴Set中的态射。范畴使用类来制定,通常是适当的类,因此您不能将范畴传递给函数。

在Haskell类型的范畴中称为* -> *中的对象为Haskell类型范畴中的态射。其中一个子范畴是Haskell类型之间的functor范畴,称为Functor

在Haskell中,每个成员类型为*的typeclass是否对应于某些类别(技术上:Hask的一个子范畴)?

是的。 这是真的,但并不是非常有趣。只需从Hask中删除不在您的typeclass中的每个对象,并删除Hask中不消耗和产生来自您的typeclass元素的任何态射,就会留下Hask的一个子范畴。该范畴应该至少具有一个对象:,至少一个态射:id

类型等于或高于* -> * -> *的typeclass是否对应于某些范畴论概念?

是的。 再次说明,这并不是非常有趣。假设有一个带有类型为* -> * -> *的typeclass X

X是否是具有相同类别的typeclasses的一个对象?嗯,是的。但这个范畴并不是非常有趣,因为很难想象有任何非平凡的态射。

X是否是某个范畴中的态射?不是,因为它不能组合。

X是否是将Hask中类型的子范畴映射到Hask类型上的态射的functor?可以,但我们必须知道X Y a bX Z a b相同才能将态射允许到我们的起始Hask类型的子范畴中。

这似乎对我来说不会产生任何有用的见解,这并不奇怪,因为我们并不真正了解X


结论

范畴论是那些很容易过度思考和过度应用的工具之一。如果您对范畴论本身并不感兴趣,我的建议是找到具体动机来使用它。具体的类型类(函子、镜头、单子、余单子等)有时会为您提供足够的结构或“原始数学材料”,从中您可以在范畴论中构建出有趣的证明。但是总的来说,研究类型类可能比使用它们更为抽象。


我们会称在 * -> * 中的对象为 Haskell 类型范畴中的态射。这些对象本身也是 Hask 的成员,这不也是事实吗?例如,[a] 是一种 * -> * 类型,但 [a] 也是 Hask 的成员,因为它本身就是一种类型(在这种情况下是高阶类型)。使用同样的推理,IO(高阶类型)和 IO String(具体类型)也是 Hask 的成员。这是真的吗? - George
1
我认为你在这里的困惑来自于对 [] 的使用举例。请记住,[][Int] 是不同的东西。因此,[Int] 是 Hask 中的一个对象,但 [] 是 Hask 中的一个态射。使用 [a] 有点令人困惑——就像谈论 f(x):我是在谈论函数 f 吗?还是在谈论在 x 处求值时函数 f 的值?这些是不同的概念。 - Dietrich Epp
让我们再次摆脱我们正在使用的这些额外变量。当您编写data T a时,T不在Hask中,但是T Int在Hask中。我特别避免谈论T a,因为我不确定您是否在谈论某个特定的a,一个隐含的全称量词,还是一个带有自由变量的类型公式。这是Haskell的一个相当优雅的特点:我们可以谈论TT Int,而不引入任何额外的变量,如a - Dietrich Epp
3
我们中的一些人使用“类型”来专指“某种类型的事物”。我们中的另一些人使用“类型”来表示“任何类型的事物”。许多人会不加警告地在这些立场之间摇摆不定,因此增加了宇宙中混淆的程度。 - pigworker
1
好的,我明白了。这只是一个术语约定问题。我感谢这次对话和大家的回答。 - George
显示剩余7条评论

3
您可以想象不保留它们范畴结构的类别之间的映射,但它们并不有趣。在范畴论中,我们希望使用保留结构的映射,这些映射被称为函子。
一种类型构造器 *->* 没有映射态射的规定。因此,您所能做的最好的方法是将它们解释为从 |C| 到 C 的函子,只是因为 |C| 没有非平凡的态射需要映射。
Haskell 中的Functor是一个自函子,只要它符合函子律(在 Haskell 中无法强制执行)。自函子不是它映射的范畴中的对象,因此也不是一种类型。但这也适用于态射——它们不是对象。然而,如果该范畴支持指数,则有一种表示态射作为对象的方式。Haskell 类型范畴是笛卡尔闭合的,因此支持指数。
因此,它是否还提供表示自函子的对象(类型)?据我所知,它没有。因此,函子不是 Hask(或我们称之为什么)的成员。
顺便说一下,一种形如 *->*->* 的映射可以具有作为双函子的非平凡范畴解释——从积范畴 CxC 到 C 的结构保持函子。请参见 Haskell 中 Bifunctor 的定义。

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