25

这编译得很好:

type List a = [a]

但是当我引入类约束时,编译器要求RankNTypes包含:

type List2 a = Num a => [a]

包含该扩展后,它编译得很好。为什么编译代码需要该扩展?

编辑:为什么我首先需要约束?

我从这篇文章中检查了这个 Lens 类型 ( type RefF a b = Functor f => (b -> f b) -> (a -> f a)) ,发现由于约束它实际上需要它。RankNTypesFunctor

4

1 回答 1

23

这不是标准的

在其中回答了问题

简单的答案是标准的 Haskell 不允许限定类型同义词声明,即类型同义词涉及=>. 根据2010 年报告,类型同义词声明的语法是:

type 简单 = 类型

where type,如果你低头看第 4.1.2 节,不能包含上下文。

顺便说一句,a上下文中是否存在类型变量并不重要。没有延期,GHC 拒绝

type IrrelevantConstraint a = Num Int => [a]

或者,就此而言,

type QualifiedT = Num Int => String

此外,即使允许这样的类型同义词,它也不会成为标准的 Haskell,如手动替换所示:

List2 a      === forall a.   Num a => [a]        -- Okay
List2 a -> b === forall a b. (Num a => [a]) -> b -- Not okay
a -> List2 b === forall a b. a -> Num b => [b]   -- Not okay

等等等等Maybe (List2 a)。在每种情况下,它并不是通常意义上的更高级别的类型。我添加了明确的 forall 符号,这当然也不是标准的,以强调这一事实。

相反,问题在于每种类型都不恰当地限定,因为=>出现在类型内部。同样,如果您查看 2010 年报告中有关表达式类型签名声明的部分,您会发现=>严格来说这不是类型的一部分,而是语法上不同的东西,例如:

expexp :: [上下文 =>]类型

由于List2Haskell2010 无效,因此需要一些语言扩展才能使其工作。没有特别记录RankNTypes允许限定类型同义词声明,但正如您所注意到的那样,它具有这种效果。为什么?

GHC 文档中有一个提示RankNTypes

-XRankNTypes对于在箭头右侧具有 forall 或上下文的任何类型(例如f :: Int -> forall a. a->a、 或g :: Int -> Ord a => a -> a) ,也需要该选项。这些类型在技术上是 1 级,但显然不是 Haskell-98,而且额外的标志似乎不值得费心。

g示例与我们的List2问题有关:那里没有forall,但箭头右侧有一个上下文,这是我上面给出的第三个示例。碰巧,RankNTypes也启用了第二个示例。

穿越模板 Haskell

绕了一条可跳过的弯路,Forall 先生在一个意想不到的地方被发现,并且考虑了等级和背景

我不知道声明的 Template Haskell 表示是否必然与类型检查器的内部表示或操作相关联。但是我们发现了一些不寻常的forall地方:一个我们没想到的地方,并且没有类型变量:

> import Language.Haskell.TH
> :set -XTemplateHaskell
> runQ [d|type List2 a = Num a => [a]|]
[TySynD List2_2
        [PlainTV a_3]
        (ForallT []
                 [ClassP GHC.Num.Num [VarT a_3]]
                 (AppT ListT (VarT a_3)))]

-- simpler example:
> runQ [d|type T = Num Int => Int|]
[TySynD T_0
        []
        (ForallT []
                 [ClassP GHC.Num.Num [ConT GHC.Types.Int]]
                 (ConT GHC.Types.Int))]

这里值得注意的是明显虚假的ForallT. 在 Template Haskell 中,这是有道理的,因为ForallT它是TypeCxt字段的唯一构造函数,即可以包含上下文。如果类型检查器类似地合并和约束上下文,那么影响其行为forall是有道理的。RankNTypes但真的吗?

在 GHC 中实施

在其中发现了为什么RankNTypes允许List2

我们得到的精确错误是:

Illegal polymorphic or qualified type: Num a => [a]
Perhaps you intended to use RankNTypes or Rank2Types
In the type declaration for `List2'

搜索 GHC 源代码显示此错误是在TcValidity.hs. 我们关心的入口点是checkValidType.

我们可以通过编译来验证编译器是否确实进入了那里-ddump-tc-trace;错误消息之前的最后一个调试输出是:

Starting validity check [Type constructor `List2']
checkValidType Num a => [a] :: *

继续checkValidType,我们看到,缺席RankNTypes类型同义词的 RHS 必须具有等级 0。(遗憾的是调试输出没有指定ctxt这里的值,但大概是TySynCtxt.)

上面的注释checkValidType在这种情况下定义了等级,因此:

    basic ::= tyvar | T basic ... basic

    r2  ::= forall tvs. cxt => r2a
    r2a ::= r1 -> r2a | basic
    r1  ::= forall tvs. cxt => r0
    r0  ::= r0 -> r0 | basic

该评论与 Template Haskell 实验相吻合,即 rank-0 类型不能涉及=>,并且任何涉及的类型都=>必须包含 aforall并因此是 1 或 2 级,即使 中没有类型变量也是如此forall。在中概述的术语中TcType,上下文仅出现在 sigma 类型中。

换句话说,在实施时,类型检查器拒绝 RHS,List2 因为它根据类资格将 RHS 解释为排名 1。

导致我们的错误消息的分支从这里开始。如果我理解正确,theta代表约束上下文。该do块第一行的核心是forAllAllowed rank,它听起来像。回想一下,类型同义词的 RHS 被限制为 0 级;由于不允许使用 forall,因此我们收到错误消息。

这解释了为什么要RankNTypes覆盖此限制。如果我们跟踪参数的rank来源,通过rank0incheckValidType,然后通过前面几行,我们发现RankNTypesflag基本上覆盖了rank0限制。(将这种情况与默认声明进行对比。)并且因为额外的上下文被视为排名错误,RankNTypes所以允许它。

于 2014-12-28T22:20:13.470 回答