【问题标题】:What are type quantifiers?什么是类型量词?
【发布时间】:2012-04-21 04:18:18
【问题描述】:

许多静态类型语言都具有参数多态性。例如在 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&lt;int&gt;(3)

假设我们有一个类型为forall T. T -&gt; T 的对象f。我们可以对这个对象做的首先是通过编写f&lt;Q&gt; 将类型Q 传递给它。然后我们返回一个类型为Q -&gt; Q 的值。但是,某些f 是无效的。比如这个f:

f<int> = (x => x+1)
f<T> = (x => x)

所以如果我们“调用”f&lt;int&gt;,那么我们会返回一个类型为 int -&gt; int 的值,通常如果我们“调用”f&lt;Q&gt;,那么我们会返回一个类型为 Q -&gt; Q 的值,这很好.但是,一般认为这个f 不是forall T. T -&gt; 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是所有类型的集合,-&gt;是函数的类型构造函数,=&gt;是lambda抽象:

forall(Types, t => (t -> t))

现在forall T:Type. T -&gt; T 类型的东西是一个,而不是从类型到值的函数。它是一个值,其类型是所有类型 T -> T 的交​​集,其中 T 涵盖所有类型。当我们使用这样一个值时,我们不必将它应用到一个类型上。相反,我们使用子类型判断:

id :: forall T:Type. T -> T
id = (x => x)

id2 = id :: int -> int

这会将id 向下转换为int -&gt; int 类型。这是有效的,因为int -&gt; int 也出现在无限交叉点中。

我认为这很有效,它清楚地解释了 forall 是什么以及它与 lambda 有何不同,但这个模型与我在 ML、F#、C# 等语言中看到的不兼容。例如在 F#您使用id&lt;int&gt; 来获取整数上的标识函数,这在此模型中没有意义:id 是值上的函数,而不是返回值上的函数的类型上的函数。


有类型理论知识的人能解释一下究竟什么是 forall 和存在吗? “forall 是类型级别的 lambda”在多大程度上是真的?

【问题讨论】:

标签: haskell types ocaml existential-type parametric-polymorphism


【解决方案1】:

让我分别解决你的问题。

  • 调用 forall“类型级别的 lambda”是不准确的,原因有两个。首先,它是类型 lambda,而不是 lambda 本身。其次,lambda 存在于术语级别,即使它对类型进行抽象(类型级别的 lambda 也存在,它们提供通常称为泛型类型的东西)。

  • 通用量化不一定意味着所有实例的“相同行为”。那是一种称为“参数性”的特定属性,可能存在也可能不存在。普通的多态 lambda 演算是参数的,因为您根本无法表达任何非参数行为。但是,如果您添加诸如 typecase(又名内涵类型分析)或检查型强制转换之类的结构作为其较弱的形式,那么您将失去参数化。参数化意味着好的属性,例如它允许在没有任何类型的运行时表示的情况下实现语言。它引发了非常强大的推理原则,例如参见瓦德勒的论文“免费定理!”。但这是一种权衡,有时您希望按类型分派。

  • 存在类型本质上表示类型(所谓的见证)和术语的对,有时称为。一种常见的查看方式是抽象数据类型的实现。这是一个简单的例子:

    pack (Int, (λx.x, λx.x em>)) : ∃ T。 (IntT) × (TInt)

    这是一个简单的 ADT,其表示为 Int,并且只提供两个操作(作为嵌套元组),用于将 int 转换为抽象类型 T .例如,这是模块类型理论的基础。

  • 总之,通用量化提供客户端数据抽象,而存在类型则双重提供实现者端数据抽象。

  • 作为补充说明,在所谓的 lambda cube 中,forall 和 arrow 被推广到 Π 型的统一概念(其中 T1→T2 = Π(x:T1 ).T2 和 ∀AT = Π(A:&ast;).T) 并且同样存在,并且元组可以推广到 Σ 类型(其中 T1×T2 = Σ(x:T1).T2 和 ∃AT = Σ(A :&ast;).T)。这里,类型 &ast;是“类型的类型”。

【讨论】:

  • 所以forall T. Q 类型的值是* -&gt; Q 类型的函数,原则上它可以是任何这样的函数,除了某些语言只允许您表达某个子集,服从参数化?我在“一种可能的解释……”中描述的模型是无效的,或者至少没有在类型论中使用? Π 和 Σ 似乎与我在那里描述的 forall 和存在非常相似,除了它们执行产品和标记联合而不是联合和交集?
  • 是的,差不多。在类型论中,您通常使用完全明确的证明术语,即所有乘积和总和,无论是有限的还是无限的,都带有明确的引入和消除形式。也就是说,人们还研究了没有这种明确形式的联合和交集类型的概念,但即使在有限的情况下,它们也往往具有相当复杂的元理论属性(例如,类型检查通常是不可判定的)。
  • 所以就像你可以根据传入的类型进行调度(如上面的f&lt;int&gt;f&lt;T&gt;)就可以违反参数化,你也可以以同样的方式违反existentials的信息隐藏?例如,如果你有一个包(T, (a,b)),你可以分析第一个组件,如果它是你知道的类型,你可以违反信息隐藏。例如,如果您检查了 T 是否为 Int,如果是,您现在可以读取 a 返回的值?
  • 参数是值的属性,对吧?例如,如果我们有一个值ff 是否是一个参数恒等函数是一个可观察的属性。不是说f 是一个参数标识函数与说f 具有所有类型的int -&gt; intstring -&gt; string 等类型完全相同吗? (也就是说,并不是每种类型都有不同的f,我们只有一个值f)。所以不管语言有什么语言特性,保证参数化的类型是t -&gt; t对于所有类型t的无限交集?
  • 是的,确切地说,缺乏参数会通过存在论破坏数据抽象。作为补救措施,您可以引入一种在运行时生成新类型名称的机制(如果您有兴趣,可以在我的主页上找到几篇关于这个主题的论文)。 - 不确定我是否完全遵循您的第二个问题。单个函数具有多个(甚至无限多个)类型并不意味着它在所有类型上的行为都相同。这取决于语言有哪些原语。
【解决方案2】:

补充两个已经很出色的答案。

首先,不能说forall 是类型级别的 lambda,因为在类型级别已经存在 lambda 的概念,它与 forall 不同。它出现在 F_omega 系统中,它是具有类型级计算的 System F 的扩展,例如有助于解释 ML 模块系统(F-ing modules,作者:Andreas Rossberg、Claudio Russo 和 Derek Dreyer,2010)。

在系统 F_omega 的(语法)中,您可以编写例如:

type prod =
  lambda (a : *). lambda (b : *).
    forall (c : *). (a -> b -> c) -> c

这是“类型构造函数”prod的定义,如prod a b是产品类型(a, b)的教堂编码类型。如果在类型级别存在计算,那么如果您想确保终止类型检查,则需要对其进行控制(否则您可以定义类型(lambda t. t t) (lambda t. t t)。这是通过使用“类型级别的类型系统”来完成的, 或 kind 系统。prod 将是 kind * -&gt; * -&gt; *。只有 kind * 的类型可以被值所占据,更高种类的类型只能应用于类型lambda (c : k) . .... 是一个类型级别的抽象,它不能是值的类型,并且可以以 k -&gt; ... 的任何形式存在,而 forall (c : k) . .... 对在某些类型 c : k 中具有多态性的值进行分类并且是必须是地面类*

其次,System F 的forall 与 Martin-Löf 类型理论的 Pi 类型之间有一个重要区别。在 System F 中,多态值对所有类型都做同样的事情。作为第一个近似值,您可以说forall a . a -&gt; a 类型的值将(隐式)将t 类型作为输入并返回t -&gt; t 类型的值。但这表明在这个过程中可能会发生一些计算,但事实并非如此。从道德上讲,当您将 forall a. a -&gt; a 类型的值实例化为 t -&gt; t 类型的值时,该值不会改变。有三种(相关的)思考方式:

  • System F 量化有类型擦除,你可以忘记类型,你仍然会知道程序的动态语义是什么。当我们使用 ML 类型推断将多态抽象和实例化隐含在我们的程序中时,如果您将“程序”视为将要运行的动态对象,我们并没有真正让推理引擎“填补我们程序中的漏洞”和计算。

  • forall a . foo 不是“为每个类型foo 生成foo 的实例@,而是一个单一类型foo,即“在未知类型a 中通用”。

  • 您可以将全称量化解释为无限合取,但有一个一致性条件,即所有合取都具有相同的结构,尤其是它们的证明都相同。

相比之下,Martin-Löf 类型理论中的 Pi 类型实际上更像是接受某物并返回某物的函数类型。这就是为什么它们不仅可以很容易地用于依赖于types,而且还可以依赖于terms(依赖类型)的原因之一。

一旦您担心这些形式理论的合理性,这就会产生非常重要的影响。 System F 是impredicative(一个forall-quantified 类型量化所有类型,包括它自己),它仍然是健全的原因是这种全称量化的统一性。虽然从程序员的角度来看,引入非参数结构是合理的(我们仍然可以在一般非参数语言中推理参数性),但它很快就会破坏底层静态推理系统的逻辑一致性。 Martin-Löf 谓词理论更容易证明正确并以正确的方式扩展。

有关 System F 的这种一致性/通用性方面的高级描述,请参阅 Fruchart 和 Longo 的 97 文章 Carnap's remarks on Impredicative Definitions and the Genericity Theorem。有关在存在非参数构造的情况下系统 F 故障的更多技术研究,请参阅 Robert Harper 和 John Mitchell (1999) 的Parametricity and variants of Girard's J operator。最后,从语言设计的角度来看,关于如何放弃 global 参数化以引入非参数结构但仍然能够在本地讨论参数化的描述,请参阅 George Neis 的 Non-Parametric Parametricity,德里克·德雷尔和安德烈亚斯·罗斯伯格,2011 年。

关于“计算抽象”和“统一抽象”之间区别的讨论已经在表示变量绑定器的大量工作中重新出现。绑定结构感觉像是一种抽象(并且可以通过 HOAS 风格的 lambda 抽象来建模),但具有统一的结构,使其更像是一个数据骨架,而不是一系列结果。这已经被大量讨论过,例如在 LF 社区中,在 Twelf 中的“代表性箭头”,在 Licata&Harper 的作品中的“正箭头”等。

最近有几个人在研究“无关性”的相关概念(lambda-abstractions,结果“不依赖”于参数),但仍然不完全清楚这与参数多态性的关系有多密切。一个例子是 Nathan Mishra-Linger 与 Tim Sheard 的作品(例如Erasure and Polymorphism in Pure Type Systems)。

【讨论】:

  • 是的!这正是我的意思。在 ML 中,forall a. a -&gt; a 类型意味着有一个 single 值,它同时具有 int -&gt; intstring -&gt; string 等类型。这让我感到困惑,并且仍然对建模感到有点肮脏作为一个函数,它接受一个类型并每次返回相同的值。唯一能保证它是相同值的是 ML 中没有某些特征。有一个明确的说法“这个single值具有all类型a -&gt; a”似乎更干净(例如a -&gt; a的交集,其中a的范围超过类型) .但这不存在?
  • “唯一能保证它是相同值的是 ML 中没有某些特征。”我不认为这是一个准确的描述,或者我认为这不是一个有用的描述。你总是会因为搞砸而失去一个好的财产,这并不意味着该财产没有充分的理由。系统 F 处于不一致的边缘,因为它在逻辑和计算上非常强大,因此可以预料到小的变化会破坏系统。
  • 您的印象是 System F 参数化不稳健,因为您有句法观点。如果您将该系统作为一个 Curry 风格的系统来研究,并辅以类型推导(无类型术语加上良好类型的证明),那么类型可擦除性属性会突然出现在您的面前。同样,如果您在语义上定义了类型,例如使用逻辑关系模型(参见例如 neelk 的 [向系统 F 类型添加方程式](www.cs.cmu.edu/~neelk/esop12.pdf)),您将定义 @987654358 @ 作为一个交叉点。然而,它与您要谈论的语言完全相同。
  • 对,我对 System F 本身毫无疑问,因为我看到没有任何构造依赖于类型。但是,您并不总是希望所有内容都具有参数化。编写 do 依赖于类型参数的事情通常很有用。另一方面,您不想失去 id、map 等函数的参数化。我想我的问题是:如何将参数化和参数化结合起来?部分原因是:如果像 id 这样的函数不依赖于类型,那么为什么它首先是所述类型的函数?
  • 部分动机不是来自类型,而是来自契约。价值很容易被合同参数化,因为合同就是价值。但是,如果您以这种方式编写 idid c x = x,并且您将依赖合同 (c:Contract) -&gt; (c -&gt; c) 应用到它以确保它的行为正确,这不会为您提供参数化。与其依赖无法在类型/合同上分派的语言,还需要一些其他构造来让您明确地说“我想要这里的参数化”。有一篇关于参数合同的论文,但不喜欢它的理由也同样适用。
【解决方案3】:

如果 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

【讨论】:

  • 谢谢!另一个很好的答案。确实,正如您所说,无限和和乘积与我所询问的方式完全不同,即它们导致非参数性。它们准确地说明了我对 ML 中的 forall 的关注,因为它们是一种概括,确实如果您可以在类型上调度,则可以让您创建非参数性。总和和乘积看起来非常笼统和美丽的想法,我会再读一些关于它们的:)
猜你喜欢
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2012-04-21
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多