我想知道为什么这段代码没有进行类型检查:
{-# LANGUAGE ScopedTypeVariables, Rank2Types, RankNTypes #-}
{-# OPTIONS -fglasgow-exts #-}
module Main where
foo :: [forall a. a]
foo = [1]
ghc抱怨:
Could not deduce (Num a) from the context ()
arising from the literal `1' at exist5.hs:7:7
鉴于:
Prelude> :t 1
1 :: (Num t) => t
Prelude>
似乎(Num t)上下文与arg的()上下文不匹配。我无法理解的一点是,因为()比(Num t)更通用,后者应该包括前者。这与缺少Haskell支持子类型有什么关系吗?
感谢您对此发表评论。
答案 0 :(得分:12)
你这里没有使用存在量化。你正在使用等级N类型。
这里[forall a. a]
意味着每个元素必须具有所有可能的类型(不是每个元素)。所以[undefined, undefined]
将是该类型的有效列表,基本上就是它。
要进一步扩展:如果列表的类型为[forall a. a]
,则表示所有元素都具有类型forall a. a
。这意味着任何采用任何类型参数的函数都可以将该列表的元素作为参数。如果你输入一个比forall a. a
更具体的类型的元素,那就不再适用了,所以你不能这样做。
要获取可包含任何类型的列表,您需要使用存在量化定义自己的列表类型。像这样:
data MyList = Nil | forall a. Cons a MyList
foo :: MyList
foo = Cons 1 Nil
当然,除非你将元素类型限制为至少实例化Show
,否则你不能对该类型的列表做任何事情。
答案 1 :(得分:3)
首先,您的示例甚至对我目前的GHC都没有那么远,因为您还需要启用ImpredecativeTypes
。这样做会导致警告:在下一个GHC中将简化或删除ImpredicativeTypes。所以我们在这里不是很好。尽管如此,添加适当的Num约束(foo :: [forall a. Num a => a]
)确实允许您的示例进行编译。
让我们抛开不可预知的类型,看一个更简单的例子:
data Foo = Foo (forall a. a)
foo = Foo 1
这也不会使用错误Could not deduce (Num a) from the context ()
进行编译。
为什么呢?好吧,该类型承诺,您将为Foo构造函数提供一种质量,对于任何类型a
,它都会产生a
。唯一满足这一点的是底部。另一方面,整数文字承诺,对于类Num 的任何类型a
,它会产生a
。所以类型明显不兼容。然而,我们可以进一步拉出forall,以获得您可能想要的东西:
data Foo = forall a. Foo a
foo = Foo 1
这样编译。但我们能做些什么呢?好吧,让我们尝试定义一个提取器函数:
unFoo (Foo x) = x
糟糕! Quantified type variable 'a' escapes
。所以我们可以定义它,但我们不能用它做很多有趣的事情。如果我们给出了一个类上下文,那么我们至少可以使用它上面的一些类函数。
存在时间和地点存在,包括没有上下文的那些,但它相当罕见,尤其是当你开始时。当你最终使用它们时,通常会出现在GADT的背景下,这是存在类型的超集,但是存在的方式产生的感觉非常自然。
答案 2 :(得分:2)
因为声明[forall a. a]
等同于说“我有一个列表,如果你(即计算机)选择一个类型,我保证表示所述列表的元素将是该类型。“
编译器正在“召唤你的虚张声势”,可以说是抱怨,“我知道”如果你给我一个1
,那么它的类型就在Num
类中,但你说我可以选择我想要的任何类型的列表。“
基本上,您尝试使用通用类型的值,就好像它是通用值的类型一样。但这些并不是一回事。