Haskell 类型作为一个类别

类别的定义

Haskell 类型以及类型之间的函数形成(几乎†)类别。我们对每个对象(类型)a 都有一个身份态射(函数)(id::a -> a); 和态度((.) :: (b -> c) -> (a -> b) -> a -> c)的组成,遵守范畴法:

f . id = f = id . f
h . (g . f) = (h . g) . f 

我们通常将此类别称为 Hask

同构

在范畴论中,当我们有一个具有逆的态射时,我们有一个同构,换句话说,有一个态射,它可以与它组成以产生同一性。在 **Hask 中,**这相当于有一对态射 fg,这样:

 f . g == id == g . f

如果我们在两种类型之间找到一对这样的态射,我们称它们彼此同构

一些 a 的两个同构类型的例子是 ((),a)a。我们可以构造两个态射:

f :: ((),a) -> a
f ((),x) = x

g::a -> ((),a)
g x = ((),x)

我们可以检查一下 f . g == id == g . f

函子

类别理论中的仿函数从一个类别到另一个类别,映射对象和态射。我们只处理一个类别, Hask of Haskell 类型的类别,因此我们将只看到从 HaskHask 的仿函数,那些起源和目标类别相同的仿函数被称为 endofunctors 。我们的 endofunctors 将是一个类型并返回另一个类型的多态类型:

F :: * -> *

遵守分类函子定律(保留身份和构成)相当于遵守 Haskell 仿函数法则:

fmap (f . g) = (fmap f) . (fmap g)
fmap id = id

因此,我们有,例如,[]Maybe(-> r) 是仿函数 Hask

单子

类别理论中的 monad 是 endofunctors 类别的 monoid 。这个类别有 endofunctors 作为对象 F :: * -> *和自然变换(它们之间的转换 forall a . F a -> G a)作为态射。

monoid 对象可以在 monoidal 类别上定义,并且是具有两个态射的类型:

zero :: () -> M
mappend :: (M,M) -> M

我们可以将其粗略地转换为 Hask endofunctors 的类别:

return::a -> m a
join::m (m a) -> m a 

并且,遵守 monad 法则相当于遵守分类的 monoid 对象定律。

†事实上,由于 undefined 的存在,所有类型的类以及类型之间的函数类并不严格地形成 Haskell 中的类别。通常,这可以通过简单地将 Hask 类别的对象定义为没有底值的类型来解决,这排除了非终止函数和无限值(codata)。有关此主题的详细讨论,请参见此处