Ambiguous type variable in a test for empty list

旧时模样 提交于 2021-01-20 07:09:14

问题


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 as, and gives back a list of as. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!