空列表测试中的模糊类型变量
Ambiguous type variable in a test for empty list
考虑以下代码段,它定义了一个函数 foo
,该函数接收一个列表并对该列表执行一些操作(如排序)。
我试图在 ghci
:
中加载代码片段
-- a function which consumes lists and produces lists
foo :: Ord a => [a] -> [a]
foo [] = []
foo (x:xs) = xs
test1 = foo [1, 2, 3] == [2, 3]
test2 = null $ foo []
却出现以下错误:
No instance for (Ord a0) arising from a use of ‘foo’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance (Ord a, Ord b) => Ord (Either a b)
-- Defined in ‘Data.Either’
instance forall (k :: BOX) (s :: k). Ord (Data.Proxy.Proxy s)
-- Defined in ‘Data.Proxy’
instance (GHC.Arr.Ix i, Ord e) => Ord (GHC.Arr.Array i e)
-- Defined in ‘GHC.Arr’
...plus 26 others
In the second argument of ‘($)’, namely ‘foo []’
In the expression: null $ foo []
In an equation for ‘test2’: test2 = null $ foo []
问题出在表达式 test2 = null $ foo []
中。此外,从 foo
的类型定义中删除 Ord a
约束将解决问题。奇怪的是,在 interactive 模式下键入 null $ foo []
(在加载 foo
的定义之后)可以正常工作并产生预期的 true
。
我需要对此行为的明确解释。
我喜欢在 "dictionary-passing style" 中考虑类型 classes。签名
foo :: Ord a => [a] -> [a]
表示 foo
获取 Ord a
的方法字典,本质上作为参数,以及 a
的列表,并返回 [=16= 的列表]s。字典里有 (<) :: a -> a -> Bool
和它的表兄弟。当我们调用 foo
时,我们需要提供这样一个字典。这是由编译器隐式完成的。所以
foo [1,2,3]
会用到Ord Integer
字典,因为我们知道a
就是Integer
.
但是,在 foo []
中,列表可以是任何内容的列表 -- 没有确定类型的信息。但是我们仍然需要找到 Ord
字典传递给 foo
(虽然你的 foo
根本没有使用它,签名说它可以,这才是最重要的) .这就是为什么会出现模棱两可的类型错误。您可以手动指定类型,这将提供足够的信息来填充字典,像这样
null (foo ([] :: [Integer]))
或使用新的 TypeApplications
扩展名
null (foo @Integer [])
如果删除 Ord
约束,正如您观察到的那样,它会起作用,这只是因为我们不再需要提供字典。我们不再需要知道 a
是什么特定类型来调用 foo
(这对我来说有点神奇:-)。
请注意 foo ([] :: Ord a => [a])
不会 消除歧义,因为不知道您要传递哪个特定的 Ord
词典;是 Ord Int
还是 Ord (Maybe String)
等?有 no 通用 Ord
字典,所以我们必须选择,在这种情况下选择什么类型有 no 规则.而当你说 (Ord a, Num a) => [a]
时,默认指定了一种选择方式,我们选择 Integer
,因为它是 Num
class.[=43= 的特例]
foo []
在 ghci
中起作用的事实是由于 ghci
的 extended defaulting rules. It might be worth reading about type defaulting 一般而言,这肯定不是 [=67= 中最漂亮的部分],但它会在您询问的各种极端情况下出现很多。
考虑以下代码段,它定义了一个函数 foo
,该函数接收一个列表并对该列表执行一些操作(如排序)。
我试图在 ghci
:
-- a function which consumes lists and produces lists
foo :: Ord a => [a] -> [a]
foo [] = []
foo (x:xs) = xs
test1 = foo [1, 2, 3] == [2, 3]
test2 = null $ foo []
却出现以下错误:
No instance for (Ord a0) arising from a use of ‘foo’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance (Ord a, Ord b) => Ord (Either a b)
-- Defined in ‘Data.Either’
instance forall (k :: BOX) (s :: k). Ord (Data.Proxy.Proxy s)
-- Defined in ‘Data.Proxy’
instance (GHC.Arr.Ix i, Ord e) => Ord (GHC.Arr.Array i e)
-- Defined in ‘GHC.Arr’
...plus 26 others
In the second argument of ‘($)’, namely ‘foo []’
In the expression: null $ foo []
In an equation for ‘test2’: test2 = null $ foo []
问题出在表达式 test2 = null $ foo []
中。此外,从 foo
的类型定义中删除 Ord a
约束将解决问题。奇怪的是,在 interactive 模式下键入 null $ foo []
(在加载 foo
的定义之后)可以正常工作并产生预期的 true
。
我需要对此行为的明确解释。
我喜欢在 "dictionary-passing style" 中考虑类型 classes。签名
foo :: Ord a => [a] -> [a]
表示 foo
获取 Ord a
的方法字典,本质上作为参数,以及 a
的列表,并返回 [=16= 的列表]s。字典里有 (<) :: a -> a -> Bool
和它的表兄弟。当我们调用 foo
时,我们需要提供这样一个字典。这是由编译器隐式完成的。所以
foo [1,2,3]
会用到Ord Integer
字典,因为我们知道a
就是Integer
.
但是,在 foo []
中,列表可以是任何内容的列表 -- 没有确定类型的信息。但是我们仍然需要找到 Ord
字典传递给 foo
(虽然你的 foo
根本没有使用它,签名说它可以,这才是最重要的) .这就是为什么会出现模棱两可的类型错误。您可以手动指定类型,这将提供足够的信息来填充字典,像这样
null (foo ([] :: [Integer]))
或使用新的 TypeApplications
扩展名
null (foo @Integer [])
如果删除 Ord
约束,正如您观察到的那样,它会起作用,这只是因为我们不再需要提供字典。我们不再需要知道 a
是什么特定类型来调用 foo
(这对我来说有点神奇:-)。
请注意 foo ([] :: Ord a => [a])
不会 消除歧义,因为不知道您要传递哪个特定的 Ord
词典;是 Ord Int
还是 Ord (Maybe String)
等?有 no 通用 Ord
字典,所以我们必须选择,在这种情况下选择什么类型有 no 规则.而当你说 (Ord a, Num a) => [a]
时,默认指定了一种选择方式,我们选择 Integer
,因为它是 Num
class.[=43= 的特例]
foo []
在 ghci
中起作用的事实是由于 ghci
的 extended defaulting rules. It might be worth reading about type defaulting 一般而言,这肯定不是 [=67= 中最漂亮的部分],但它会在您询问的各种极端情况下出现很多。