法律如何帮助验证haskell中类别的含义

时间:2017-06-28 15:18:07

标签: haskell functor category-theory

haskell中的所有类别(Monoid,Functor,Monad等)都有相应的法律,实例必须满足这些法律才能保证类别按预期工作。我无法理解的是,如何为一个类别选择特定的法律?例如,选择id&的原因是什么?仿函数的成分定律?

2 个答案:

答案 0 :(得分:6)

leftaroundabout稍微纠正了你的术语,但要详细说明:类别理论中的类别是一个抽象,包括对象,这些对象之间的转换,以及组合这些转换的能力(这个组合的构成)是联想的)。我们不经常在haskell中讨论类别本身,但是我们使用的一些抽象来自类别理论,我们称之为在我们称之为Hask的类别中或多或少地生活 EM>。在Control.Category中有 category 抽象的表示;它是(.)的更一般版本。

但我现在会忘记名为 category 的抽象。需要理解的重要一点是,我们在haskell中表达 category 等抽象的方式是使用类型类

你问为什么法律是他们的方式。良好抽象的标志是一般来说足以广泛适用,同时仍然给我们一些有用的东西

我认为Monoid是最好的例子。列表,集合,关于加法(和乘法)的整数,从事物到幺半群等的函数都形成一个幺半群:即我们可以定义mempty和{{1}的实现}这符合您可以阅读的有关here的法律,因此它可以广泛应用。

但是法律给了我们什么?法律允许我们在选择monoid 时编写更多有用的代码 generic。例如,知道mappend是关联的,我们可以定义一个:

mappend

我们的用户不需要关心是使用mconcat :: [a] -> a 还是foldr或其他方式来实现这一点。这也意味着编译器可以根据上下文自由选择折叠的最佳顺序。我们有一个标识对象这一事实意味着可以在空列表中安全地调用此函数。

幺半群定律在分布式计算的背景下也是必不可少的,例如:你有map-reduce模式,其中monoidal reduce操作可以在你的分布式"列表中自由应用。#/ p>

因此,当你放弃法律时,你会得到一些不太有用的东西。但为什么这些法律?我认为简单的答案是因为人们发现它们很有用。还有一种情况是,放松或删除法律可以创建一些更通用的,并且在不同的环境中也有用。例如,在haskell中我们有foldl类,或者在数学中你有非欧几里德几何。

答案 1 :(得分:4)

对玩家的漫长颂歌

  

Eilenberg& amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; and Mac Lane(1945)题为“自然对等的一般理论”。我们说“差不多”,因为他们早期的论文(1942)包含特定的算子和工作中的自然变换,仅限于群体。为了澄清和抽象他们的1942年结果,Eilenberg& amp; Mac Lane设计类别理论。正如其标题所示,当时的核心概念是自然变革。为了给出后者的一般定义,他们定义了算子,从Carnap借用了这个术语,为了定义仿函数,他们从亚里士多德,康德和CS皮尔士的哲学中借用了“类别”这个词,但重新定义了它数学上。

因此,Stanford Encyclopedia of Philosophy是一个非常好的资源,如果你想深入研究早期的CT论文。本文,"自然对等通用理论," (我会说)是必读的。 (You can circumvent the academic paywalls by searching for the title on Google, but here's a link that may or may not die soon.

我们以Functor为例。这三个法律是

  • 每个f a -> f b都存在a -> b(我们将其表示为类型为(a -> b) -> f a -> f b的定理,我们通过在Haskell值术语中定义函数来证明它)
  • fmap id = id
  • fmap (f . g) = fmap f . fmap g

类型类Functor旨在以这种方式表示分类仿函数:如果存在实例Functor f,那么我们可以将f视为来自F > Hask 到 F(Hask)。仅凭这一点并没有用,但是类别理论有这种方法可以诱使你从简单,琐碎的数学对象属性中证明强大的一般结果。

这三条法律直接来自Eilenberg和Mac Lane的原始定义:

screenshot

符号过时,但想法是一样的。

但是,再次,为什么?为什么我们在类别理论中将这个基本思想放在这些对象,箭头,身份,构图和分布的概念上?一个无奈的答案是他们的工作。"曾经如此定义的函数出现在各处:组,向量空间,拓扑,图形,Reader/State/IO,在CT本身内自引用。它们只是具体到足以允许各种属性的证明,而只是足以允许这些被证明的属性变得有趣和有用。

更有帮助的答案可能是阅读Eilenberg和Mac Lane的论文的介绍,他们在那里仔细地为他们将要做的工作(几乎无处出现!)留出空间。它们采用向量空间L及其共轭空间T(L);他们注意到你可以证明两者之间的同构性,但前提是你能确定基础。然后他们取L及其迭代的共轭空间T(T(L));现在这里是一个同构,它存在于所有有限L 而没有任何特殊基础。他们称之为“自然”,#34;与平等概念相关的优雅概念。一旦你注意到这一点,你就会开始在任何地方看到它。

但是要开始讨论这个问题,首先需要为从LT(L)映射到T(T(L))的事物命名。并且该名称及其定义必须足够广泛,以适用于组和拓扑以及您手头上的任何其他内容。所以这是一个算子。其法律是从每个领域的现有法律中采用的。在软件工程术语中,我们可以说仿函数是数学定律的重构。

这里对Haskell编程的类比是,有些函数在其他语言中一直出现。您会看到mapMaybe :: (a -> b) -> Maybe a -> Maybe bmapPromise :: (a -> b) -> Promise a -> Promise bmapState :: (a -> b) -> State r a -> State r b。也许不是那些确切的形式;也许作为泛型类的方法,如果你被困在OOP土地上。每个库函数都有重复的东西。所有类型的宇宙之间似乎存在自然等价,然后Maybe / State r / Promise的宇宙应用于所有这些类型。您甚至可能会注意到,具有代数数据类型的语言可以像Ed Kmett所指出的那样automatically generate these functions

那么,例如,为什么我们坚持在映射中保留身份?那么它可以让你自动为每个ADT生成一个唯一的fmap。它使等式推理更容易,因为如果您看到fmap id,则可以自动替换id。或者相反,如果您看到id,则可以自动替换fmap id。它适用于fmap的自由定理。还有一连串的其他原因。只有凡人才有所回报。

所以,在另一种语言中,我们会记录或博客关于这个相同外观map*函数的集合,作为神秘生活的古玩。但是对于类型类和强类型,我会看到它,参数化我们实际上可以用像Haskell这样的语言编码这个想法。这就是Haskell的精彩之处。