为什么多态类型为`forall t:Type,t-> t`的函数是恒等函数?


18

我是编程语言理论的新手。我正在看一些在线讲座,其中讲师声称多态类型的功能forall t: Type, t->t是身份,但没有解释原因。有人可以向我解释原因吗?也许可以证明第一原则的主张。


3
我以为这个问题一定是重复的,但我找不到。cs.stackexchange.com/questions/341/…是一种后续措施。标准参考是免费的定理!由Phil Wadler撰写。
吉尔斯(Gillles)“所以-别再作恶了”

1
尝试使用这种类型的泛型函数构造其他功能。您会发现没有。
Bergi

@Bergi是的,我找不到任何反例,但仍不确定如何证明它。
Abhishek

但是,当您尝试找到一个时,您的观察结果是什么?为什么您进行的任何尝试都不起作用?
Bergi

@Gilles也许您还记得cs.stackexchange.com/q/19430/14663
Bergi

Answers:


32

首先要注意的是,这不一定是正确的。例如,根据语言的不同,具有该类型的功能除了作为标识功能外,还可以:1)永远循环,2)改变某些状态,3)返回null,4)抛出异常,5)执行一些I / O, 6)分叉线程来做其他事情,7)做call/cc恶作剧,8)使用类似Java的东西Object.hashCode,9)使用反射来确定类型是否为整数,如果是,则将其递增,10)使用反射来分析调用栈,根据被调用的上下文来做某事,11)可能还有很多其他事情,当然也可以是上述内容的任意组合。

因此,导致这种情况的特性,即参量性,是整个语言的特性,并且它的变化形式越来越强。对于类型理论研究的许多形式结石,以上行为均不会发生。例如,对于系统F /首先研究了参量的纯多态Lambda演算,上述任何行为都不会发生。它根本没有例外,可变的状态,nullcall/cc,I / O,反思,我们强烈正常化,因此它不能永远循环下去。正如吉尔斯在评论中提到的,论文定理是免费的!菲尔·沃德勒(Phil Wadler)的著作对这一主题进行了很好的介绍,其参考文献将进一步深入该理论,特别是逻辑关系技术。该链接还列出了Wadler的其他一些关于参数化的论文。

由于参量性是语言的一种属性,因此要证明其参数性,首先需要对语言进行形式化表达,然后再进行相对复杂的论证。假设我们处于多态lambda演算中,这种特殊情况的非正式论点是,由于我们一无所知,t因此无法对输入执行任何操作(例如,我们无法对其进行增量运算,因为我们不知道它是否为一个数字)或创建该类型的值(对于我们所知的t= Void,根本没有任何值的类型)。产生类型值的唯一方法t是返回给我们的值。没有其他行为是可能的。观察这种情况的一种方法是使用强归一化并显示只有一个这种类型的范式项。


1
系统F如何避免类型系统无法检测到的无限循环?在一般情况下,这是无法解决的。
约书亚

2
@Joshua-暂停问题的标准不可能证明始于假设首先存在无限循环的假设。因此,提出质疑为什么系统F没有无限循环是循环推理。更广泛地说,系统F 几乎没有图灵完整,因此我怀疑它满足该证明的任何假设。它很弱,足以使计算机证明其所有程序都已终止(没有递归,没有while循环,对于循环来说非常弱,等等)。
乔纳森·

@Joshua:在一般情况下是无法解决的,这并不排除在许多特殊情况下都可以解决。特别是,每个恰好是类型正确的系统F术语的程序都被证明已停止运行:有一个统一的证明适用于所有这些程序。显然,这意味着还有其他程序无法在系统F中键入...
Cody

15

要求的证明非常复杂,但是如果您确实要这么做,则可以查看雷诺兹关于该主题的原始论文

关键思想是它适用于参数多态函数,其中对于该函数的所有单态实例化,多态函数的主体都是相同的。在这样的系统中,无法假设多态类型的参数的类型,并且如果作用域中唯一的值具有泛型,则除了将其返回或将其传递给您所使用的其他函数外,与它无关。已经定义,那么除了返回或通过它之外什么也不能做。因此,最后,您只能做一些标识函数链,然后返回参数。


8

有了Derek提到的所有警告,并忽略了使用集合论产生的悖论,让我本着雷诺兹/沃德勒的精神来画一个证明。

函数类型:

f :: forall t . t -> t

是按类型t索引的函数的族。ftt

这个想法是,要正式定义多态函数,我们不应将类型视为值的集合,而应视为关系。基本类型(如Int归纳相等关系)-例如,Int如果两个值相等,则它们是相关的。如果函数将相关值映射到相关值,则它们是相关的。有趣的情况是多态函数。他们将相关类型映射到相关值。

fg

forall t . t -> t

stfsfssstgtttfgfsgt

fstfsft

()()t()t((), c)ctf()ftf()()()ftcc()cftidttfid

您可以在我的博客中找到更多详细信息。


-2

编辑:上面的评论提供了缺少的一块。有些人刻意玩的语言不尽人意。我明确地不在乎这些语言。真正有用的,没有图灵的语言是很难设计的。其余所有内容都将尝试将这些定理应用于完整的语言时发生了什么。

假!

function f(a): forall t: Type, t->t
    function g(a): forall t: Type, t->t
       return (a is g) ? f : a
    return a is f ? g : a

其中is运算符比较参考同一性两个变量。也就是说,它们包含相同的值。不是等效值,相同值。函数fg在某些定义上是等效的,但它们并不相同。

如果该函数本身被传递,则返回其他内容。否则返回其输入。其他事物与自身具有相同的类型,因此可以替换。换句话说,f不是身份,因为f(f)回报率g,而身份将返回f

为使该定理成立,它必须假定可笑的还原能力

function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(1000, <0, a>)[1]¹

如果您愿意假设可以假设可以轻松处理类型推断。

如果我们试图限制该域直到定理成立,我们最终将其限制得太远了。

  • 纯功能(无可变状态,无IO)。好吧,我可以忍受。很多时候我们想对函数进行证明。
  • 空的标准库。嗯
  • raise,不exit。现在我们开始受到束缚。
  • 没有底部类型。
  • 该语言有一个规则,允许编译器通过假定必须终止来终止无限递归。允许编译器拒绝琐碎的无限递归。
  • 如果出现无法通过任何方式证明的问题,则允许编译器失败。²现在,标准库无法将函数用作参数。嘘
  • 没有nil。这开始出现问题。我们用尽了所有方法来处理1 /0.³
  • 该语言不能执行分支类型推断,并且对于程序员何时可以证明语言不能证明的类型推断没有覆盖。这很糟糕。

最后两个约束的存在削弱了该语言。尽管Turing仍然完整,但摆脱通用工作的唯一方法是模拟一个内部平台,该平台解释具有较宽松要求的语言。

¹如果您认为编译器可以推断出这一点,请尝试这一点

function fermat(z) : int -> int
    function pow(x, p)
        return p = 0 ? 1 : x * pow(x, p - 1)
    function f2(x, y, z) : int, int, int -> <int, int>
        left = pow(x, 5) + pow(y, 5)
        right = pow(z, 5)
        return left = right
            ? <x, y>
            : pow(x, 5) < right
                ? f2(x + 1, y, z)
                : pow(y, 5) < right
                    ? f2(2, y + 1, z)
                    : f2(2, 2, z + 1)
    return f2(2, 2, z)
function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(fermat(3)[0], <0, a>)[1]

²编译器无法执行此操作的证明取决于盲目性。我们可以使用多个库来确保编译器无法一次看到循环。同样,我们总是可以在程序可以运行但无法编译的地方进行构建,因为编译器无法在可用内存中进行归纳。

³有人认为您可以使此返回值为nil,而没有任何泛型类型返回nil。这是一个令人讨厌的惩罚,我没有看到任何有效的语言可以为此付出代价。

function f(a, b, c): t: Type: t[],int,int->t
    return a[b/c]

一定不能编译。基本问题是运行时数组索引不再起作用。


@Bergi:我构造了一个反例。
约书亚

1
请花点时间思考一下您的答案与其他两个答案之间的区别。德里克(Derek)的开场白是“首先要注意的是,这不一定是正确的”。然后他解释了一种语言的哪些特性使之成为现实。jmite还解释了使其成为现实的原因。相反,您的答案给出了一个未指定(且不常见的语言)且解释为零的示例。(foil无论如何,量词是什么?)这根本没有帮助。
吉尔(Gilles)'所以

1
@DW:如果a为f,则a的类型为f的类型,也是f的类型,因此应该通过typecheck。如果真正的编译器将其踢出,我将使用真实语言始终具有的静态类型系统的运行时强制转换,将其弄错,并且它在运行时不会失败。
约书亚记

2
这不是静态类型检查器的工作方式。它不检查单个输入的类型是否匹配。有特定的类型规则,旨在确保该功能将对所有可能的输入进行类型检查。如果您需要使用类型转换,那么这种解决方案就没那么有趣了。当然,如果您绕过类型系统,那么函数的类型将无法提供任何保证,这不足为奇!
DW

1
@DW:您错过了重点。静态类型检查器有足够的信息来证明代码具有智能才能找到的类型安全。
约书亚记
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.