许多静态类型语言具有参数多态性。例如在 C# 中可以定义:
T Foo<T>(T x){ return x; }
在呼叫站点中,您可以执行以下操作:
int y = Foo<int>(3);
这些类型有时也写成这样:
Foo :: forall T. T -> T
我听说有人说“forall 就像类型级别的 lambda 抽象”。所以 Foo 是一个函数,它接受一个类型(例如 int)并产生一个值(例如一个 int -> int 类型的函数)。许多语言推断类型参数,以便您可以编写Foo(3)
而不是Foo<int>(3)
.
假设我们有一个f
类型为 的对象forall T. T -> T
。我们可以对这个对象做的首先是Q
通过写给它一个类型f<Q>
。然后我们返回一个 type 的值Q -> Q
。但是,某些f
' 是无效的。例如这个f
:
f<int> = (x => x+1)
f<T> = (x => x)
因此,如果我们“调用” f<int>
,那么我们会返回一个带有 type 的值,int -> int
一般来说,如果我们“调用” f<Q>
,那么我们会返回一个带有 type 的值Q -> Q
,所以这很好。但是,通常认为这f
不是 type 的有效事物,因为根据您传递的类型forall T. T -> T
,它会做不同的事情。forall 的想法是明确不允许这样做。另外,如果 forall 是类型级别的 lambda,那么存在什么?(即存在量化)。由于这些原因,似乎 forall 和 exists 并不是真正的“类型级别的 lambda”。但那它们是什么?我意识到这个问题相当模糊,但有人可以帮我解决这个问题吗?
一个可能的解释如下:
如果我们看逻辑,量词和 lambda 是两个不同的东西。量化表达式的一个例子是:
forall n in Integers: P(n)
所以有两部分要考虑:一个用于量化的集合(例如整数)和一个谓词(例如 P)。Forall 可以看作是一个高阶函数:
forall n in Integers: P(n) == forall(Integers,P)
带类型:
forall :: Set<T> -> (T -> bool) -> bool
Exists 具有相同的类型。Forall 就像一个无限合取,其中 S[n] 是集合 S 的第 n 个元素:
forall(S,P) = P(S[0]) ∧ P(S[1]) ∧ P(S[2]) ...
Exists 就像一个无限析取:
exists(S,P) = P(S[0]) ∨ P(S[1]) ∨ P(S[2]) ...
如果我们对类型进行类比,我们可以说∧的类型类比是计算交集类型∩,∨的类比类型是计算并集类型∪。然后我们可以定义 forall 和存在于类型上,如下所示:
forall(S,P) = P(S[0]) ∩ P(S[1]) ∩ P(S[2]) ... exists(S,P) = P(S[0]) ∪ P(S[1]) ∪ P(S[2]) ...
所以forall是一个无限的交集,exists是一个无限的并集。他们的类型是:
forall, exists :: Set<T> -> (T -> Type) -> Type
例如多态恒等函数的类型。这
Types
是所有类型的集合,->
是函数的类型构造函数,=>
是 lambda 抽象:forall(Types, t => (t -> t))
现在类型的东西
forall T:Type. T -> T
是一个值,而不是从类型到值的函数。它是一个值,其类型是所有类型 T -> T 的交集,其中 T 涵盖所有类型。当我们使用这样的值时,我们不必将其应用于类型。相反,我们使用子类型判断:id :: forall T:Type. T -> T id = (x => x) id2 = id :: int -> int
这向下转换
id
为有 typeint -> int
。这是有效的,因为int -> int
也出现在无限交集。我认为这很好用,它清楚地解释了 forall 是什么以及它与 lambda 的不同之处,但这个模型与我在 ML、F#、C# 等语言中看到的不兼容。例如在 F# 中你
id<int>
可以获取整数上的标识函数,这在此模型中没有意义:id 是值上的函数,而不是返回值上的函数的类型上的函数。
有类型理论知识的人可以解释一下到底什么是 forall 和存在的吗?“forall 在类型级别上是 lambda”在多大程度上是真的?