什么是skolems?
Eeek! GHCi在我的代码中find了Skolems!
... Couldn't match type `k0' with `b' because type variable `b' would escape its scope This (rigid, skolem) type variable is bound by the type signature for groupBy :: Ord b => (a -> b) -> Set a -> Set (b, [a]) The following variables have types that mention k0 ...
他们是什么? 他们想要什么与我的程序? 为什么他们试图逃脱(忘恩负义的小型拳击手)?
首先,上下文中的“刚性”typesvariables表示由该上下文之外的量词绑定的typesvariables,因此不能与其他typesvariables统一。
这很像一个lambda绑定的variables:给定一个lambda (\x -> ... )
,从“外部”,当然可以将它应用于任何你喜欢的值。 但在内部,你不能简单地认定x
的值应该是某个特定的值。 为lambda内的x
select一个值应该听起来很愚蠢,但这就是关于“无法匹配等等等等,刚性typesvariables,等等等等”的错误。
请注意,即使不使用显式全量量词,任何顶级types签名对于提到的每个typesvariables都有一个隐含的说明。
当然,这不是你得到的错误。 什么是“转义typesvariables”意味着更傻 – 这就像有一个lambda (\x -> ...)
并尝试使用lambda 之外的x
具体值,独立于将其应用于参数。 不,不会将lambda应用于某些内容并使用结果值 – 我的意思是实际上使用variables本身在它所定义的范围之外。
这种情况可能发生在types上(没有像使用lambda的例子那样显然是荒谬的)是因为有两个“typesvariables”的概念是浮动的:在统一过程中,有“variables”代表未确定的types,然后被识别与其他这样的variables通过types推断。 另一方面,你有上面描述的量化typesvariables,这些variables具体被确定为可能的types。
考虑lambdaexpression式的types(\x -> x)
。 从一个完全不确定的typesa
,我们看到它只需要一个参数,把它缩小到a -> b
,那么我们就可以看到它必须返回与它的参数types相同的东西,所以我们把它进一步缩小到a -> a
。 但现在它适用于任何types,所以我们给它一个量词(forall a. a -> a)
。
所以,当你有一个量词的范围时,GHC推断应该与该量词范围之外的未确定types统一起来。
所以显然我忘了在这里解释“skolemtypesvariables”这个词,嘿。 正如评论中所提到的,在我们的例子中,它基本上是“刚性typesvariables”的同义词,所以上面仍然解释了这个想法。
我不完全确定这个术语是从哪里来的,但我猜想它涉及到Skolem的正常forms,并且代表了存在性的量化,就像在GHC中所做的那样。 skolem(或刚性)types的variables是在某个范围内由于某种原因具有未知但特定types的variables,它是来自存在性数据types&c的多态types的一部分。
据我所知,一个“Skolemvariables”是一个variables,它不匹配任何其他variables,包括它本身。
当你使用显式合并,GADT和其他types的系统扩展时,这似乎会在Haskell中popup。
例如,请考虑以下types:
data AnyWidget = forall x. Widget x => AnyWidget x
这说的是,你可以采取任何实现Widget
类的types,并将其包装成AnyWidget
types。 现在,假设你试图解开这个:
unwrap (AnyWidget w) = w
嗯,不,你不能这样做。 因为在编译时我们不知道w
是什么types,所以没有办法为此写一个正确的types签名。 在这里, w
的types已经从AnyWidget
“逃脱”了,这是不允许的。
据我所知,内部GHC给出了一个types,这是一个Skolemvariables,来表示它不能逃脱的事实。 (这不是唯一的这种情况,还有其他一些地方,由于input问题,某些价值无法逃脱。
当一个typesvariables试图转义它的作用域时popup错误消息。
我花了一段时间才弄清楚这个,所以我会写一个例子。
{-# LANGUAGE ExistentialQuantification #-} data I a = I a deriving (Show) data SomeI = forall a. MkSomeI (I a)
那么如果我们尝试写一个函数
unI (MkSomeI i) = i
GHC拒绝types推断/types检查这个function。
为什么? 让我们尝试自己推断types:
-
unI
是一个lambda定义,所以对于某些types的x
和y
,它的types是x -> y
y
。 -
MkSomeI
有一个types的forall a. I a -> SomeI
forall a. I a -> SomeI
-
MkSomeI i
有一个typesSomeI
-
i
在LHS有一个typesI z
types。 由于所有的量词,我们不得不引入新的(新鲜的)types的variables。 请注意,这不是通用的,因为它被绑定在(SomeI i)
expression式中。 - 因此我们可以用
SomeI
统一typesvariablesx
,这是可以的。 所以unI
应该inputSomeI -> y
。
-
-
i
在RHS上也有typesI z
。 - 在这一点上,统一者试图统一
y
和I z
,但是它注意到z
是在较低的上下文中引入的。 因此它失败了。
否则, unI
的types将会键入forall z. SomeI -> I z
forall z. SomeI -> I z
,但是正确的是exists z. SomeI -> I z
exists z. SomeI -> I z
。 然而,这个GHC不能直接表示。
同样,我们可以看到为什么
data AnyEq = forall a. Eq a => AE a -- reflexive :: AnyEq -> Bool reflexive (AE x) = x == x
作品。
AE x
中的(存在variables)variables不会转移到外部范围内,所以一切正常。
另外我在GHC 7.8.4和7.10.1中遇到了一个“特征” ,其中RankNTypes
本身是可以的,但是添加GADTs
会触发错误
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE GADTs #-} example :: String -> I a -> String example str x = withContext xs where si = "Foo" ++ str withContext :: I a -> (forall b. I b -> c) -> c withContext xf = fx
所以你的代码可能没有问题。 它可能是GHC,它不能一致地把所有东西都弄清楚。
编辑 :解决办法是给一个typess :: forall a. I a -> String
s :: forall a. I a -> String
。
GADTs
打开MonoLocalBinds
,这使得s
推断types有skolemvariables,所以types不是全部forall a. I a -> String
forall a. I a -> String
,但t -> String
,被绑定在错误的上下文。 请参阅: https : //ghc.haskell.org/trac/ghc/ticket/10644