Haskell 理解Hindley-Milner型推理中的多型

Haskell 理解Hindley-Milner型推理中的多型,haskell,type-inference,type-systems,lambda-calculus,hindley-milner,Haskell,Type Inference,Type Systems,Lambda Calculus,Hindley Milner,我正在读维基百科的一篇文章,试图从中找到一些意义。到目前为止,我的理解是: 类型分为单型或多型 单类型进一步分类为类型常量(如int或string)或类型变量(如α和β) 类型常量可以是具体类型(如int和string)或类型构造函数(如Map和Set) 类型变量(如α和β)充当具体类型的占位符(如int和字符串) 现在我在理解多型方面有点困难,但在学习了Haskell之后,这就是我对它的理解: 类型本身有类型。形式上类型的类型称为种类(即有不同种类的类型) 具体类型(如int和string)和

我正在读维基百科的一篇文章,试图从中找到一些意义。到目前为止,我的理解是:

  • 类型分为单型或多型
  • 单类型进一步分类为类型常量(如
    int
    string
    )或类型变量(如
    α
    β
  • 类型常量可以是具体类型(如
    int
    string
    )或类型构造函数(如
    Map
    Set
  • 类型变量(如
    α
    β
    )充当具体类型的占位符(如
    int
    字符串
  • 现在我在理解多型方面有点困难,但在学习了Haskell之后,这就是我对它的理解:

  • 类型本身有类型。形式上类型的类型称为种类(即有不同种类的类型)
  • 具体类型(如
    int
    string
    )和类型变量(如
    α
    β
    )属于
    *
    类型
  • 类型构造函数(如
    Map
    Set
    )是类型的lambda抽象(例如
    Set
    是类
    *->*
    Map
    是类
    *->*->*
  • 我不明白的是限定词意味着什么。例如,
    ∀α、 σ
    表示什么?我似乎无法理解它的头绪,我越是读下面的一段,就越感到困惑:

    具有polytype∀α、 相比之下,α->α可以将相同类型的任何值映射到自身,并且是该类型的值。另一个例子是∀α、 (集合α)->int是将所有有限集合映射为整数的函数类型。成员计数是此类型的值。请注意,限定符只能出现在顶层,即类型∀α.α -> ∀α、 例如,类型的语法排除了α,而单类型包含在多类型中,因此类型具有一般形式∀α₁ . . . ∀αₙ.τ


    首先,种类和多态类型是不同的。你可以有一个HM类型系统,其中所有类型都是相同的(*),你也可以有一个没有多态性但类型复杂的系统

    如果术语
    M
    属于
    ∀a、 t
    ,这意味着对于任何类型的
    s
    ,我们都可以在
    t
    中用
    s
    代替
    a
    (通常写为
    t[a:=s]
    ,我们将得到
    M
    t[a:=s]类型的
    。这有点类似于逻辑,在逻辑中,我们可以用任何术语替换通用量化变量,但这里我们讨论的是类型

    这正是Haskell中发生的情况,只是在Haskell中你看不到量词。类型签名中出现的所有类型变量都是隐式量化的,就像你在类型前面有
    forall
    一样。例如,
    map
    会有类型

    map::全部a.全部b.(a->b)->[a]->[b]
    
    等等。如果没有这种隐含的通用量化,类型变量
    a
    b
    将必须具有某种固定的含义,并且
    map
    不会是多态的

    HM算法区分类型(不带量词、单类型)和类型模式(通用量化类型、多类型)。重要的是,在某些地方它使用类型模式(如在
    let
    ),但在其他地方只允许使用类型。这使整件事可以判定


    我还建议您阅读这篇文章。这是一个更复杂的系统,它允许所有类型中的
    都是
    (因此所有类型都称为type),但类型推断/检查是不可判定的。它可以帮助您了解所有
    的工作原理。Girard、Lafont和Taylor对系统F进行了深入描述。

    考虑Haskell中的
    l=\x->t
    。它是一个lambda,表示一个术语
    t
    ,它包含一个变量
    x
    ,稍后将被替换(例如,
    l1
    ,无论它是什么意思)。同样地,
    ∀α、 σ
    表示具有类型变量
    α
    的类型,即
    f:∀α、 σ
    如果由类型
    α
    参数化的函数。在某种意义上,
    σ
    依赖于
    α
    ,因此
    f
    返回类型
    σ(α)
    的值,其中
    α
    将在
    σ(α)
    中替换,我们将得到一些具体的类型

    在Haskell中,您可以省略
    并定义类似于
    id:a->a
    的函数。允许省略量词的原因基本上是因为它们只允许在顶层使用(没有
    RankNTypes
    扩展)。您可以尝试以下代码:

    id2 : a -> a -- I named it id2 since id is already defined in Prelude
    id2 x = x
    
    如果您向ghci询问
    id
    :t id
    )的类型,它将返回
    a->a
    。更准确地说(更符合类型理论),
    id
    具有
    ∀a、 a->a
    。现在,如果向代码中添加:

    val = id2 3
    

    ,3具有类型
    Int
    ,因此类型
    Int
    将被替换为
    σ
    ,我们将得到关于Haskell中类型量化的具体类型
    Int->Int
    ,可以成为一个有价值的发现。系统F的类型推理是不可判定的,但类型检查很容易(如果通过类型检查,我们的意思是用类型对术语进行注释,我们只是检查这些注释是否有意义)。@augustss通过类型检查,意味着您获得了一个未注释的术语(咖喱风格)还有一个类型,你应该确定这个术语是否符合这个类型。这也是不可判定的,正如Joe Wells在《佩特普德拉克》中所证明的那样。@PetrPudlák我知道这是不可判定的。很多人把类型检验称为检验教堂风格术语是否有效的过程,所以