Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 斯科勒姆是什么?_Haskell_Types_Type Variables - Fatal编程技术网

Haskell 斯科勒姆是什么?

Haskell 斯科勒姆是什么?,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,

哎呀!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, [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拒绝使用此函数进行类型推断/类型检查


为什么??让我们自己来推断类型:

  • unI
    是lambda定义,因此对于某些类型
    x
    y
    ,它的类型是
    x->y
  • MkSomeI
    的类型为所有a。I a->SomeI
    • MkSomeI i
      具有类型
      SomeI
    • 对于某些类型的
      z
      ,LHS上的
      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