编译好:
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
。
答案 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 Report和expression type signatures上的2010年报告部分,您会发现=>
严格来说并不是类型的一部分,但是而是一个语法上截然不同的东西,例如:
exp → exp
::
[ context=>
] type
由于List2
是无效的Haskell2010,因此需要一些语言扩展才能使其工作。没有具体记录RankNTypes
允许限定类型的同义词声明,但是你已经注意到它具有这种效果。为什么呢?
对于箭头右侧带有forall或上下文的任何类型,都需要
-XRankNTypes
选项(例如f :: Int -> forall a. a->a
或g :: Int -> Ord a => a -> a
)。这些类型在技术上排名第一,但显然不是Haskell-98,额外的标志似乎不值得打扰。
g
示例与我们的List2
问题有关:那里没有forall
,但箭头右侧有一个上下文,这是我上面给出的第三个例子。碰巧的是,RankNTypes
也启用了第二个例子。
在可以跳过的迂回行走中,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
会影响其行为。但是吗?
在其中发现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
,我们看到,缺席RankNTypes
,checkValidType
。 (不幸的是,调试输出在这里没有指定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
允许它。