问题
Consider the following snippet which defines a function foo
which takes in a list and performs some operation on the list (like sorting).
I tried to load the snippet in 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 []
yet the following error occurs:
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 []
The problem is in the expression test2 = null $ foo []
. Furthermore, removing Ord a
constraint from the type definition of foo
will solve the problem. Strangely, typing null $ foo []
in the interactive mode (after loading the definition for foo
) works correctly and produces the expected true
.
I need a clear explanation for this behaviour.
回答1:
I like thinking of typeclasses in "dictionary-passing style". The signature
foo :: Ord a => [a] -> [a]
says that foo
takes a dictionary of methods for Ord a
, essentially as a parameter, and a list of a
s, and gives back a list of a
s. The dictionary has things in it like (<) :: a -> a -> Bool
and its cousins. When we call foo
, we need to supply such a dictionary. This is done implicitly by the compiler. So
foo [1,2,3]
will use the Ord Integer
dictionary, because we know that a
is Integer
.
However, in foo []
, the list could be a list of anything -- there is no information to determine the type. But we still need to find the Ord
dictionary to pass to foo
(although your foo
doesn't use it at all, the signature says that it could, and that's all that matters). That's why there is an ambiguous type error. You can specify the type manually, which will give enough information to fill in the dictionary, like this
null (foo ([] :: [Integer]))
or with the new TypeApplications
extension
null (foo @Integer [])
If you remove the Ord
constraint, it works, as you have observed, and this is just because we no longer need to supply a dictionary. We don't need to know what specific type a
is to call foo
anymore (this feels a little magical to me :-).
Note that foo ([] :: Ord a => [a])
does not eliminate the ambiguity, because it is not known which specific Ord
dictionary you want to pass; is it Ord Int
or Ord (Maybe String)
, etc.? There is no generic Ord
dictionary, so we have to choose, and there is no rule for what type to choose in this case. Whereas when you say (Ord a, Num a) => [a]
, then defaulting specifies a way to choose, and we pick Integer
, since it is a special case of the Num
class.
The fact that foo []
works in ghci
is due to ghci
’s extended defaulting rules. It might be worth reading about type defaulting in general, which is surely not the prettiest part of Haskell, but it is going to come up a lot in the kinds of corner cases you are asking about.
来源:https://stackoverflow.com/questions/49339042/ambiguous-type-variable-in-a-test-for-empty-list