许多静态类型语言都具有参数多态性。例如,在C#中,可以定义:
T Foo<T>(T x){ return x; }
在通话网站中,您可以这样做:
int y = Foo<int>(3);
这些类型有时也是这样编写的:
Foo :: forall T. T -> T
我听过有人说“forall就像类型级别的lambda抽象”。所以Foo是一个函数,它接受一个类型(例如int),并产生一个值(例如int类型的函数 - &gt; int)。许多语言推断出类型参数,因此您可以编写Foo(3)
而不是Foo<int>(3)
。
假设我们有f
类型的对象forall T. T -> T
。我们可以对此对象执行的操作是首先通过编写Q
将其传递给f<Q>
类型。然后我们返回类型为Q -> Q
的值。但是,某些f
无效。例如f
:
f<int> = (x => x+1)
f<T> = (x => x)
因此,如果我们“调用”f<int>
,那么我们会返回类型为int -> int
的值,一般情况下,如果我们“调用”f<Q>
,那么我们会返回类型为{的值{1}},这样很好。但是,通常理解这个Q -> Q
不是f
类型的有效内容,因为它根据您传递的类型执行不同。 forall的想法是明确不允许。另外,如果forall是类型级别的lambda,那么存在什么? (即存在量化)。由于这些原因,似乎forall和存在并不是真正的“类型级别的lambda”。但那他们是什么?我意识到这个问题很模糊,但有人可以为我清楚这一点吗?
可能的解释如下:
如果我们看一下逻辑,量词和lambda是两回事。量化表达的一个例子是:
forall T. T -> T
因此,forall有两个部分:一组量化(例如整数)和一个谓词(例如P)。 Forall可被视为更高阶函数:
forall n in Integers: P(n)
使用类型:
forall n in Integers: P(n) == forall(Integers,P)
Exists具有相同的类型。 Forall就像一个无限的结合,其中S [n]是集合S的第n个元素:
forall :: Set<T> -> (T -> bool) -> bool
存在就像一个无限的分离:
forall(S,P) = P(S[0]) ∧ P(S[1]) ∧ P(S[2]) ...
如果我们对类型进行类比,我们可以说∧的类型模拟是计算交集类型∩,以及计算联合类型的∨类型。然后我们可以定义forall并在类型上存在如下:
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 :: Set<T> -> (T -> Type) -> Type
是所有类型的集合,Types
是函数的类型构造函数,->
是lambda抽象:=>
现在
forall(Types, t => (t -> t))
类型的东西是值,而不是从类型到值的函数。它是一个值,其类型是所有类型T - &gt;的交集。 T其中T范围超过所有类型。当我们使用这样的值时,我们不必将其应用于类型。相反,我们使用子类型判断:forall T:Type. T -> T
向下转发
id :: forall T:Type. T -> T id = (x => x) id2 = id :: int -> int
以获得id
类型。这是有效的,因为int -> int
也出现在无限交集中。我认为这很好用,它清楚地解释了forall是什么以及它与lambda的不同之处,但是这个模型与我在ML,F#,C#等语言中看到的不兼容。例如在F#中你做
int -> int
来获取int的标识函数,这在这个模型中是没有意义的:id是值的函数,而不是返回值函数的函数的函数。
具有类型理论知识的人能否解释究竟是什么样的存在?并且“forall在类型级别是lambda”的程度是多少?
答案 0 :(得分:15)
让我分别解答你的问题。
由于两个原因,调用forall“类型级别的lambda”是不准确的。首先,是 lambda的类型,而不是lambda本身。其次,lambda存在于术语级别,即使它抽象了类型(类型级别的lambda也存在,它们提供通常称为泛型类型的东西)。
通用量化并不一定意味着所有实例化都具有“相同的行为”。这是一种称为“参数”的特殊属性,可能存在也可能不存在。普通的多态lambda演算是参数化的,因为你根本无法表达任何非参数行为。但是如果你添加像typecase(a.k.a。内涵类型分析)这样的结构或者检查强化形式的构造,那么你就会失去参数。参数化意味着很好的属性,例如它允许在没有任何类型的运行时表示的情况下实现语言。它引出了非常强大的推理原则,例如, Wadler的论文“免费定理!”。但这是一种权衡,有时候你想要分发类型。
存在类型本质上表示一种类型(所谓的见证)和一个术语,有时称为包。查看这些内容的一种常见方法是实现抽象数据类型。这是一个简单的例子:
pack( Int ,(λ x 。 x ,λ x 。 x )):∃ T 。 ( Int → T )×( T → Int )
这是一个简单的ADT,其表示形式为 Int ,它只提供两个操作(作为嵌套元组),用于转换内插和抽出类型的内容 T 。例如,这是模块类型理论的基础。
总之,通用量化提供了客户端数据抽象,而存在类型提供了实现者端数据抽象。
作为补充说明,在所谓的 lambda cube 中,forall和arrow被推广到Π型的统一概念(其中T1→T2 =Π(x:T1) ).T2和∀AT=Π(A:*)。T)同样存在,并且可以将tupling推广到Σ-型(其中T1×T2 =Σ(x:T1).T2和∃AT=Σ(A: *)。T)。这里,类型*是“类型的类型”。
答案 1 :(得分:7)
如果forall是lambda ...,那么存在什么
为什么,当然是元组!
在Martin-Löf type theory中,你有Π类型,对应于函数/通用量化和Σ-类型,对应于元组/存在量化。
它们的类型与您提出的类型非常相似(我在这里使用Agda表示法):
Π : (A : Set) -> (A -> Set) -> Set
Σ : (A : Set) -> (A -> Set) -> Set
实际上,Π是无穷乘积,Σ是无穷和。请注意,它们不是“交集”和“联合”,正如您所提议的那样,因为如果不另外定义类型相交的位置,则无法执行此操作。 (一种类型的值对应于另一种类型的值)
从这两个类型构造函数中,您可以拥有所有正常,多态和依赖函数,正常和依赖元组,以及存在和普遍量化的语句:
-- Normal function, corresponding to "Integer -> Integer" in Haskell
factorial : Π ℕ (λ _ → ℕ)
-- Polymorphic function corresponding to "forall a . a -> a"
id : Π Set (λ A -> Π A (λ _ → A))
-- A universally-quantified logical statement: all natural numbers n are equal to themselves
refl : Π ℕ (λ n → n ≡ n)
-- (Integer, Integer)
twoNats : Σ ℕ (λ _ → ℕ)
-- exists a. Show a => a
someShowable : Σ Set (λ A → Σ A (λ _ → Showable A))
-- There are prime numbers
aPrime : Σ ℕ IsPrime
然而,这根本不涉及参数性,AFAIK参数和Martin-Löf型理论是独立的。
对于参数化,人们通常会引用Philip Wadler's work。