当我尝试在GHC 7.4.1下加载以下代码时:
{-# LANGUAGE RankNTypes #-}
import Control.Monad.ST
newtype M s a = M { unM :: ST s a }
runM :: (forall s. M s a) -> a
runM (M m) = runST m
它失败并显示以下消息:
test.hs:9:14:
Couldn't match type `s0' with `s'
because type variable `s' would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: ST s a
The following variables have types that mention s0
m :: ST s0 a (bound at test.hs:9:9)
In the first argument of `runST', namely `m'
In the expression: runST m
In an equation for `runM': runM (M m) = runST m
当M
只是ST
的包装时,为什么会失败?
(我的实际程序有一些堆叠在顶部的变压器 - 这只是一个小例子。)
编辑:似乎添加let
修复了此问题:
runM m = let M m' = m in runST m
但是,如果启用了TypeFamilies
(就像在我的真实代码中那样),它会再次失败。
答案 0 :(得分:11)
模式匹配+ rankntypes是个问题。
GHC推断m
类型为ST ??? a
,其中???
是一个类型变量,可以与任何东西统一,并且必须与某些东西统一*。然后,我们将其传递给runST
,runST
想要ST s a
,m
与???
统一,s
与s
统一。但是等等,现在我们在s
范围之外与test (M m) = (m :: forall t . ST t a) `seq` ()
进行统一,其中m
被定义范围如此灾难。
一个更简单的例子是
t
我们再次收到同样的错误,因为我们尝试使用t
与 test m = runST (unM m)
类型统一,但unM
的范围太小。
最简单的解决方案就是不使用
创建此类型变量ST
此处runST
会返回let
满意的良好且真实的多态-XTypeFamilies
。您可以使用m
,因为默认情况下它是多态的,但由于let
会让单态变形,它会像您发现的模式匹配一样爆炸。
**似乎test :: forall a. (forall t. M t a) -> ()
test (M m) = (m :: ST Bool a) `seq` (m :: ST Int a) `seq` ()
是单态的。 Bool
是多态的,没有类型族,所以我怀疑这是正在发生的事情。它表现得像
Int
尝试统一{{1}}和{{1}}的错误正如您对单形类型变量所期望的那样。为什么我发现的每个奇怪的类型错误似乎都隐藏了某种形式的单态变量..