简体   繁体   English

Haskell中存在量化值的列表

[英]List of existentially quantified values in Haskell

I'm wondering why this piece of code doesn't type-check: 我想知道为什么这段代码没有进行类型检查:

{-# LANGUAGE ScopedTypeVariables, Rank2Types, RankNTypes #-}
{-# OPTIONS -fglasgow-exts #-}

module Main where

foo :: [forall a. a]
foo = [1]

ghc complains: ghc抱怨:

Could not deduce (Num a) from the context ()
  arising from the literal `1' at exist5.hs:7:7

Given that: 鉴于:

Prelude> :t 1
1 :: (Num t) => t
Prelude> 

it seems that the (Num t) context can't match the () context of arg. 似乎(Num t)上下文与arg的()上下文不匹配。 The point I can't understand is that since () is more general than (Num t), the latter should and inclusion of the former. 我无法理解的一点是,因为()比(Num t)更通用,后者应该包括前者。 Has this anything to do with lack of Haskell support for sub-typing? 这与缺少Haskell对子类型的支持有什么关系吗?

Thank you for any comment on this. 感谢您对此发表评论。

You're not using existential quantification here. 你这里没有使用存在量化。 You're using rank N types. 您正在使用等级N类型。

Here [forall a. a] 在这里[forall a. a] [forall a. a] means that every element must have every possible type (not any, every). [forall a. a]意味着每个元素必须具有所有可能的类型(不是每个元素)。 So [undefined, undefined] would be a valid list of that type and that's basically it. 所以[undefined, undefined]将是该类型的有效列表,基本上就是这样。

To expand on that a bit: if a list has type [forall a. a] 要扩展一点:如果列表有类型[forall a. a] [forall a. a] that means that all the elements have type forall a. a [forall a. a]这意味着所有元素都有类型forall a. a forall a. a . forall a. a That means that any function that takes any kind of argument, can take an element of that list as argument. 这意味着任何采用任何类型参数的函数都可以将该列表的元素作为参数。 This is no longer true if you put in an element which has a more specific type than forall a. a 如果您放入一个比forall a. a更具体类型的元素,则不再适用forall a. a forall a. a , so you can't. forall a. a ,所以你不能。

To get a list which can contain any type, you need to define your own list type with existential quantification. 要获取可包含任何类型的列表,您需要使用存在量化定义自己的列表类型。 Like so: 像这样:

data MyList = Nil | forall a. Cons a MyList
foo :: MyList
foo = Cons 1 Nil

Of course unless you restrain element types to at least instantiate Show , you can't do anything with a list of that type. 当然除非你限制元素类型至少实例化Show ,否则你不能对该类型的列表做任何事情。

First, your example doesn't even get that far with me for the current GHC, because you need to enable ImpredecativeTypes as well. 首先,您的示例甚至对我当前的GHC都没有那么远,因为您还需要启用ImpredecativeTypes Doing so results in a warning that ImpredicativeTypes will be simplified or removed in the next GHC. 这样做会导致警告:在下一个GHC中将简化或删除ImpredicativeTypes。 So we're not in good territory here. 所以我们在这里不是很好。 Nonetheless, adding the proper Num constraint ( foo :: [forall a. Num a => a] ) does allow your example to compile. 尽管如此,添加适当的Num约束( foo :: [forall a. Num a => a] )确实允许您的示例进行编译。

Let's leave aside impredicative types and look at a simpler example: 让我们抛开不可预知的类型,看一个更简单的例子:

data Foo = Foo (forall a. a)
foo = Foo 1

This also doesn't compile with the error Could not deduce (Num a) from the context () . 这也无法编译错误Could not deduce (Num a) from the context ()

Why? 为什么? Well, the type promises that you're going to give the Foo constructor something with the quality that for any type a , it produces an a . 好吧,类型承诺你将给Foo构造函数一个质量,对于任何类型a ,它产生a The only thing that satisfies this is bottom. 唯一满足这一点的是底部。 An integer literal, on the other hand, promises that for any type a that is of class Num it produces an a . 文字的整数,在另一方面,承诺,对于任何类型的a 是类货号它产生a So the types are clearly incompatible. 所以类型明显不兼容。 We can however pull the forall a bit further out, to get what you probably want: 然而,我们可以进一步拉出forall,以获得您可能想要的东西:

data Foo = forall a. Foo a
foo = Foo 1

So that compiles. 所以编译。 But what can we do with it? 但我们能做些什么呢? Well, let's try to define an extractor function: 好吧,让我们尝试定义一个提取器函数:

unFoo (Foo x) = x

Oops! 哎呀! Quantified type variable 'a' escapes . Quantified type variable 'a' escapes So we can define that, but we can't do much interesting with it. 所以我们可以定义它,但我们不能用它做很多有趣的事情。 If we gave a class context, then we could at least use some of the class functions on it. 如果我们给出了一个类上下文,那么我们至少可以使用它上面的一些类函数。

There is a time and place for existentials, including ones without class context, but its fairly rare, especially when you're getting started. 存在时间和地点存在,包括没有类背景的那些,但它相当罕见,特别是当你开始时。 When you do end up using them, often it will be in the context of GADTs, which are a superset of existential types, but in which the way that existentials arise feels quite natural. 当你最终使用它们时,它通常会出现在GADT的背景下,它是存在类型的超集,但存在的方式出现的感觉非常自然。

Because the declaration [forall a. a] 因为声明[forall a. a] [forall a. a] is (in meaning) the equivalent of saying, "I have a list, and if you (ie the computer) pick a type, I guarantee that the elements of said list will be that type." [forall a. a] (意思是)相当于说“我有一个列表,如果你(即计算机)选择一个类型,我保证所述列表的元素将是那种类型。”

The compiler is "calling your bluff", so-to-speak, by complaining, "I 'know' that if you give me a 1 , that its type is in the Num class, but you said that I could pick any type I wanted to for that list." 通过抱怨,“我知道”如果你给我一个1 ,它的类型是在Num类,但是你说我可以选择任何类型,编译器正在“调用你的虚张声势”。想要那份名单。“

Basically, you're trying to use the value of a universal type as if it were the type of a universal value. 基本上,您尝试使用通用类型的值,就好像它是通用值的类型一样。 Those aren't the same thing, though. 但这些并不是一回事。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM