Haskell中的列表是归纳还是诱导?

时间:2016-10-04 14:09:11

标签: haskell infinite idris induction coinduction

所以我最近一直在阅读有关coinduction的内容,现在我想知道:Haskell列出归纳还是共同?我也听说过Haskell并没有区分这两者,但如果是这样的话,他们又怎么这样做呢?

列表是归纳定义的,data [a] = [] | a : [a],但可以共同使用,ones = a:ones。我们可以创建无限列表。然而,我们可以创建有限列表。他们是哪一个?

相关的是Idris,其中类型List a严格来说是归纳类型,因此只是有限列表。它的定义类似于Haskell中的方式。但是,Stream a是一种coinductive类型,为无限列表建模。它被定义为(或者更确切地说,定义等同于)codata Stream a = a :: (Stream a)。创建无限列表或有限流是不可能的。但是,当我写定义

codata HList : Type -> Type where
    Nil : HList a
    Cons : a -> HList a -> HList a

我得到了我对Haskell列表的期望,即我可以创建有限和无限结构。

所以,让我把它们归结为几个核心问题:

  1. Haskell不区分归纳和共感类型吗?如果是这样,那是什么形式化?如果没有,那么这是[a]?

  2. HList是coinductive吗?如果是这样,coinductive类型如何包含有限值?

  3. 如果我们定义data HList' a = L (List a) | R (Stream a)怎么办?只考虑HList

  4. 会考虑什么和/或它会有用?

3 个答案:

答案 0 :(得分:24)

  1. 由于懒惰,Haskell类型既是归纳的又是共同的,或者,数据和codata之间没有正式的区别。所有递归类型都可以包含构造函数的无限嵌套。在诸如Idris,Coq,Agda等语言中,诸如urllib的定义被终止检查器拒绝。懒惰意味着Wget Beautiful Soup Urllib Requests 可以一步到ones = 1 : ones进行评估,而其他语言仅评估为普通形式,ones没有正常形式。

  2. ' Coinductive'并不意味着“必然是无限的”,它的意思是“它是如何被解构的”,而是由它的构造方式所定义的归纳手段'。我认为this是对细微差别的极好解释。当然你会同意类型

    1 : ones

    不能无限。

  3. 这是一个有趣的 - 而不是ones,你永远不会知道'如果它是有限的或无限的(具体来说,你可以在有限的时间内发现一个列表是有限的,但是你无法计算它是无限的),codata A : Type where MkA : A给你一个简单的方法来决定它在恒定的时间内如果你的名单是有限的或无限的。

答案 1 :(得分:18)

在像Coq或Agda这样的总语言中,归纳类型是那些在有限时间内可以拆除其价值的类型。电感功能必须终止。另一方面,共同类型是那些在有限时间内可以建立的值。共同功能必须富有成效。

旨在用作证明助理(如Coq和Agda)的系统必须是完全的,因为非终止导致系统在逻辑上不一致。但是要求所有函数都是完全的和归纳的,这使得无法使用无限结构,因此,发明了共同诱导。

因此归纳和共感类型的目的是拒绝可能的非终止程序。这是Agda中一个功能的例子,由于生产力条件而被拒绝。 (传递给filter的函数可能会拒绝每个元素,因此您可能会永远等待生成的流的下一个元素。)

filter : {A : Set} -> (A -> Bool) -> Stream A -> Stream A
filter f xs with f (head xs)
... | true = head xs :: filter f (tail xs)
... | false = filter f (tail xs)  -- unguarded recursion

现在,Haskell没有归纳或共感类型的概念。问题是“这种类型是归纳型还是共同型?”不是一个有意义的。 Haskell如何在不进行区分的情况下逃脱?好吧,Haskell从来没有打算在一开始就作为逻辑保持一致。它是一种部分语言,这意味着您可以编写非终止和非生产函数 - 没有终止检查程序,也没有生产力检查程序。人们可以辩论这个设计决策的智慧,但它肯定会使感应和共同诱导变得多余。

相反,Haskell程序员习惯于非正式地推理程序的终止/生产力。懒惰让我们可以使用无限的数据结构,但是我们没有得到机器的任何帮助以确保我们的功能是完整的。

答案 2 :(得分:5)

要解释类型级递归,需要找到一个“固定点” CPO值列表编写器

F X = (1 + A_bot * X)_bot

如果我们以归纳的方式推理,我们希望固定点为“最小”。如果是共同的,“最伟大的”。

从技术上讲,这是通过在CPO_bot的嵌入投影子类别中工作来完成的,例如,对于“最少”嵌入图表的colimit

0_bot |-> F 0_bot |-> F (F 0_bot) |-> ...

推广Kleene的不动点定理。对于“最大”,我们将采用预测图的限制

0_bot <-| F 0_bot <-| F (F 0_bot) <-| ...

然而事实证明,对于任何F,“最小”与“最大”同构。这是“bilimit”定理(参见Abramsky的“领域理论”调查论文)。

也许令人惊讶的是,结果是感应或共同的味道来自F而不是最小/最大固定点所应用的提升。例如,如果x是被粉碎的产品而#是破碎的总和,

F X = 1_bot # (A_bot x X)

将有限列表(最多为iso)作为bilimit。

[我希望我能解决这些问题 - 这些很棘手;-)]