如何为脱离其范围的严格类型变量重现 GHC 的类型错误?

How to reproduce GHC's type error for a rigid type variable escaping its scope?

这是一个刚性类型变量脱离其作用域的典型例子:

{-# LANGUAGE RankNTypes #-}
runST :: forall a. (forall s. ST s a) -> a
runST _ = undefined

newRef :: forall s a. a -> ST s (Ref s a)
newRef _ = undefined

runST (newRef 'x') -- type error (rigid a would escape its scope)

我试图通过手动执行 unification/subsumption 来重现错误:

-- goal: unify the expression `runST (newRef 'x')`
fun :: (forall a. (forall b. Foo b a) -> a)
arg :: (forall c. Foo c (Bar c Char))
(forall b. Foo b a0) -> a0 -- meta-ize all tvs bound by the outermost quantifier
Foo s0 (Bar s0 Char) -- skolemize all tvs bound by the outermost quantifier
-- subsumption judgement: offered `arg` is at least as polymorphic as fun's requested argument:
Foo s0 (Bar s0 Char) <: (forall b. Foo b a0)
Foo s0 (Bar s0 Char) <: Foo s1 a0 -- skolemize on the RHS
Foo ~ Foo -- generativity of Foo
s0 ~ s1 -- injectivity of Foo's arguments
-- REJECTED: cannot instantiate `s0` with `s1`
-- we don't even reach the point where the escape check is taken place:
a0 ~ (Bar s0 Char) -- would fail due to s0 ~ s1

据我所知,刚性类型变量(skolem 常量)只能归入自身或与不允许前者脱离其范围的元(统一)类型变量统一,即 meta tv 必须在刚性电视最初被 skolemized 的范围内不是免费的。因此,s0 ~ s1 应该被拒绝,因为两个不同的骨架不能被实例化,对吧?

然而,正如我所解释的错误信息,GHC到达了转义检查。是我的统一步骤有误还是我误解了错误消息?

写下我的评论作为答案,因为我猜它有帮助。 :)

我认为你的错误是在应用程序中对参数进行了 skolemising;相反,它应该用一个新的元变量实例化(我将为元变量写一个抑扬符,为 skolem 常量写一个粗体):

fun : ∀a. (∀b. Foo b a) → a

arg : (∀c. Foo c (Bar c Char))

[0/a]((∀b. Foo b a) → a) = (∀b. Foo b 0) → 0

Foo c (Bar c Char)

[1/c](Foo c (Bar c Char)) = Foo 1 (Bar 1 Char)

然后,在子类型判断中:

… ⊢ Foo 1 (Bar 1 Char) ≤: ∀b. Foo b 0

你还是像往常一样skolemise了右边的∀绑定变量:

…, b ⊢ Foo 1 (Bar 1 Char) ≤: Foo b 0

这根据生成性给出 Foo = Foo,但是 求解 â1b通过单射性,求解â0到Barâ1 字符。当退出量词的范围时,这会正确地给出发生检查失败,因为替换的类型 [â1=b ](Bar â1 Char) = Bar b Char 包含 skolem 常数b.

我说“skolemise”是因为它确实引入了一个类型常量,但它也可以被认为只是进入量词的范围(如在“Complete and Easy”中),不一定执行深度 skolemisation。