我是编程语言理论的新手。我正在看一些在线讲座,其中讲师声称多态类型的功能forall t: Type, t->t
是身份,但没有解释原因。有人可以向我解释原因吗?也许可以证明第一原则的主张。
我是编程语言理论的新手。我正在看一些在线讲座,其中讲师声称多态类型的功能forall t: Type, t->t
是身份,但没有解释原因。有人可以向我解释原因吗?也许可以证明第一原则的主张。
Answers:
首先要注意的是,这不一定是正确的。例如,根据语言的不同,具有该类型的功能除了作为标识功能外,还可以:1)永远循环,2)改变某些状态,3)返回null
,4)抛出异常,5)执行一些I / O, 6)分叉线程来做其他事情,7)做call/cc
恶作剧,8)使用类似Java的东西Object.hashCode
,9)使用反射来确定类型是否为整数,如果是,则将其递增,10)使用反射来分析调用栈,根据被调用的上下文来做某事,11)可能还有很多其他事情,当然也可以是上述内容的任意组合。
因此,导致这种情况的特性,即参量性,是整个语言的特性,并且它的变化形式越来越强。对于类型理论研究的许多形式结石,以上行为均不会发生。例如,对于系统F /首先研究了参量的纯多态Lambda演算,上述任何行为都不会发生。它根本没有例外,可变的状态,null
,call/cc
,I / O,反思,我们强烈正常化,因此它不能永远循环下去。正如吉尔斯在评论中提到的,论文定理是免费的!菲尔·沃德勒(Phil Wadler)的著作对这一主题进行了很好的介绍,其参考文献将进一步深入该理论,特别是逻辑关系技术。该链接还列出了Wadler的其他一些关于参数化的论文。
由于参量性是语言的一种属性,因此要证明其参数性,首先需要对语言进行形式化表达,然后再进行相对复杂的论证。假设我们处于多态lambda演算中,这种特殊情况的非正式论点是,由于我们一无所知,t
因此无法对输入执行任何操作(例如,我们无法对其进行增量运算,因为我们不知道它是否为一个数字)或创建该类型的值(对于我们所知的t
= Void
,根本没有任何值的类型)。产生类型值的唯一方法t
是返回给我们的值。没有其他行为是可能的。观察这种情况的一种方法是使用强归一化并显示只有一个这种类型的范式项。
有了Derek提到的所有警告,并忽略了使用集合论产生的悖论,让我本着雷诺兹/沃德勒的精神来画一个证明。
函数类型:
f :: forall t . t -> t
是按类型t索引的函数的族。
这个想法是,要正式定义多态函数,我们不应将类型视为值的集合,而应视为关系。基本类型(如Int
归纳相等关系)-例如,Int
如果两个值相等,则它们是相关的。如果函数将相关值映射到相关值,则它们是相关的。有趣的情况是多态函数。他们将相关类型映射到相关值。
forall t . t -> t
f
s
t
()
()
t
()
t
((), c)
c
t
()
()
c
c
()
c
t
f
id
您可以在我的博客中找到更多详细信息。
编辑:上面的评论提供了缺少的一块。有些人刻意玩的语言不尽人意。我明确地不在乎这些语言。真正有用的,没有图灵的语言是很难设计的。其余所有内容都将尝试将这些定理应用于完整的语言时发生了什么。
假!
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
运算符比较参考同一性两个变量。也就是说,它们包含相同的值。不是等效值,相同值。函数f
和g
在某些定义上是等效的,但它们并不相同。
如果该函数本身被传递,则返回其他内容。否则返回其输入。其他事物与自身具有相同的类型,因此可以替换。换句话说,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]¹
如果您愿意假设可以假设可以轻松处理类型推断。
如果我们试图限制该域直到定理成立,我们最终将其限制得太远了。
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]
一定不能编译。基本问题是运行时数组索引不再起作用。
foil
无论如何,量词是什么?)这根本没有帮助。