Haskell 斯科勒姆是什么?
哎呀!GHCi在我的代码中找到了SkolemsHaskell 斯科勒姆是什么?,haskell,types,type-variables,Haskell,Types,Type Variables,哎呀!GHCi在我的代码中找到了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,
...
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
...
它们是什么?他们想要我的节目做什么?
他们为什么要逃避(忘恩负义的小坏蛋)?首先,上下文中的“刚性”类型变量意味着由上下文之外的量词绑定的类型变量,因此不能与其他类型变量统一
这与lambda绑定的变量非常相似:给定lambda(\x->…)
,当然,从“外部”可以将其应用于您喜欢的任何值;但是在内部,您不能简单地决定x
的值应该是某个特定的值。在lambda中为x
选择一个值听起来很愚蠢,但这就是错误“不能匹配废话,刚性类型变量,废话”的意思
请注意,即使不使用显式forall
量词,任何顶级类型签名对于提到的每个类型变量都有一个隐式forall
当然,这不是你得到的错误。“转义类型变量”的含义更为愚蠢——这就像拥有一个lambda(\x->…)
,并尝试在lambda之外使用x
的特定值,而不将其应用于参数。不,不将lambda应用于某个对象并使用结果值——我的意思是在定义变量的范围之外实际使用变量本身
这可能发生在类型上的原因(不像lambda的例子那样明显荒谬)是因为“类型变量”有两个概念:在统一过程中,有代表未确定类型的“变量”,然后通过类型推断与其他此类变量标识。另一方面,您有上面描述的量化类型变量,这些变量被专门标识为在可能的类型范围内
考虑lambda表达式的类型(\x->x)
。从一个完全不确定的类型a
开始,我们看到它接受一个参数并将其缩小到a->b
,然后我们看到它必须返回与参数相同类型的内容,所以我们进一步将其缩小到a->a
。但现在它适用于您可能需要的任何类型a
,因此我们给它一个量词(对于所有a.a->a)
因此,当您有一个由量词绑定的类型,GHC推断该类型应该与该量词范围之外的未确定类型统一时,就会出现转义类型变量
很明显,我忘了在这里解释术语“skolem类型变量”,呵呵。正如评论中提到的,在我们的例子中,它本质上是“刚性类型变量”的同义词,所以上面仍然解释了这个想法 我不完全确定这个术语起源于何处,但我猜它涉及并代表了普遍意义上的存在量化,正如GHC中所做的那样。skolem(或刚性)类型变量是指在某个范围内,由于某种原因而具有未知但特定类型的变量。作为多态类型的一部分,它来自于一个存在的数据类型,&c。据我所知,“skolem变量”是一个不匹配任何其他变量(包括其本身)的变量 当您使用诸如显式孔、GADT和其他类型系统扩展之类的特性时,Haskell中似乎会出现这种情况
例如,考虑以下类型:
data AnyWidget = forall x. Widget x => AnyWidget x
这意味着您可以采用实现小部件
类的任何类型,并将其包装为任何小部件
类型。现在,假设您尝试打开此文件:
不,你不能那样做。因为在编译时,我们不知道w
的类型是什么,所以无法为此编写正确的类型签名。此处,w
类型已从AnyWidget
中“转义”,这是不允许的
据我所知,GHC内部给出了
w
一个类型,它是一个Skolem变量,表示它不能转义的事实。(这不是唯一的这种情况;还有一些地方由于键入问题而无法转义某个值。)当类型变量试图转义其作用域时,会弹出错误消息
我花了一段时间才弄明白这一点,所以我将写一个例子
{-# LANGUAGE ExistentialQuantification #-}
data I a = I a deriving (Show)
data SomeI = forall a. MkSomeI (I a)
那么如果我们试着写一个函数
unI (MkSomeI i) = i
GHC拒绝使用此函数进行类型推断/类型检查
为什么??让我们自己来推断类型:
是lambda定义,因此对于某些类型unI
和x
,它的类型是y
x->y
的类型为所有a。I a->SomeIMkSomeI
具有类型MkSomeI i
SomeI
- 对于某些类型的
,LHS上的z
具有类型i
。由于i z
量词,我们不得不引入新的(新鲜的)类型变量。注意,它不是通用的,因为它绑定在forall
表达式中(SomeI i)
- 因此,我们可以将类型变量
与x
统一起来,这样就可以了。因此,SomeI
应该具有类型unI
SomeI->y
- 因此,RHS上的
也具有类型i
i z
- 此时,unifier试图统一
和y
,但它注意到iz
是在较低的上下文中引入的。因此它失败了z
unI
的类型将具有所有z的类型。有些人->我z
,但正确的是存在z。SomeI->iz
。但这一全球温室气体排放委员会不能直接代表全球温室气体排放
同样,我们也可以看出原因
data AnyEq = forall a. Eq a => AE a
-- reflexive :: AnyEq -> Bool
reflexive (AE x) = x == x
工作
aex
中的(存在)变量没有逃逸到外部范围,因此一切正常
另外,我在GHC 7.8.4和7.10.1中遇到了一个错误,其中
RankNTypes
本身是可以的,但是添加GADTs
会触发错误
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
example :: String -> I a -> String
example str x = withContext x s
where
s i = "Foo" ++ str
withContext :: I a -> (forall b. I b -> c) -> c
withContext x f = f x
所以你的电脑可能没什么问题
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
example :: String -> I a -> String
example str x = withContext x s
where
s i = "Foo" ++ str
withContext :: I a -> (forall b. I b -> c) -> c
withContext x f = f x