为什么类型同义词中的类约束需要RankNTypes

时间:2014-04-08 18:28:21

标签: haskell lens higher-rank-types

编译好:

type List a = [a]

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

type List2 a = Num a => [a]

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

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

我正在检查来自this post的此镜头类型(type RefF a b = Functor f => (b -> f b) -> (a -> f a)),并发现由于RankNTypes约束,它确实需要Functor

1 个答案:

答案 0 :(得分:22)

这不是标准

回答问题

简单的答案是标准的Haskell不允许限定类型的同义词声明,即涉及=>的类型同义词。根据{{​​3}},类型同义词声明的语法是:

  

type simpletype = type

其中 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)等等。在每种情况下,它是通常意义上的更高等级类型。我强调了这个事实,我添加了明确的标记,当然也不是标准的。

相反,问题是每种类型都不合格,因为=>出现在类型中。同样,如果您查看2010 Reportexpression type signatures上的2010年报告部分,您会发现=>严格来说并不是类型的一部分,但是而是一个语法上截然不同的东西,例如:

  

exp exp :: [ context =>] type

由于List2是无效的Haskell2010,因此需要一些语言扩展才能使其工作。没有具体记录RankNTypes允许限定类型的同义词声明,但是你已经注意到它具有这种效果。为什么呢?

declarations

  

对于箭头右侧带有forall或上下文的任何类型,都需要-XRankNTypes选项(例如f :: Int -> forall a. a->ag :: Int -> Ord a => a -> a)。这些类型在技术上排名第一,但显然不是Haskell-98,额外的标志似乎不值得打扰。

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

通过Template Haskell

的一次旅行

在可以跳过的迂回行走中,Forall先生被发现在一个意想不到的地方,并且考虑了等级和背景

我不知道声明的模板Haskell表示是否必然与typechecker的内部表示或操作相关联。但是我们发现了一些不寻常的东西:我们不期望的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是具有Type字段的Cxt的唯一构造函数,即可以包含上下文。如果类型检查器同样会混淆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源搜索显示此错误是在hint in the GHC docs on RankNTypes中生成的。我们关注的切入点是TcValidity.hs

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

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

继续checkValidType,我们看到,缺席RankNTypescheckValidType。 (不幸的是,调试输出在这里没有指定ctxt的值,但它可能是the RHS of a type synonym must have rank 0。)

checkValidType上方的注释在此上下文中定义了排名:

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

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

该评论与模板Haskell实验相符,即排名0类型不能涉及=>,涉及=>的任何类型必须包含forall,因此排名为1或2,即使forall中没有类型变量。在TySynCtxt中,上下文仅出现在sigma类型中。

换句话说,正如实施的那样,typechecker拒绝List2 的RHS,因为它将RHS解释为因其类别资格而排名为1。

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

这解释了为什么RankNTypes会覆盖此限制。如果我们跟踪参数rank的来源,通过here,然后通过前几行,我们发现RankNTypes标志基本上覆盖了rank0限制。 (将情况与rank0 in checkValidType进行对比。)由于额外的上下文被视为 rank 错误,RankNTypes允许它。