这不是标准的
在其中回答了问题
简单的答案是标准的 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 年报告中有关表达式类型签名和声明的部分,您会发现=>
严格来说这不是类型的一部分,而是语法上不同的东西,例如:
exp → exp ::
[上下文 =>
]类型
由于List2
Haskell2010 无效,因此需要一些语言扩展才能使其工作。没有特别记录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
它是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 源代码显示此错误是在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
来源,通过rank0
incheckValidType
,然后通过前面几行,我们发现RankNTypes
flag基本上覆盖了rank0
限制。(将这种情况与默认声明进行对比。)并且因为额外的上下文被视为排名错误,RankNTypes
所以允许它。