6
votes

Can anybody explain why haskell enforces explicit type signature in the following example and how to modify it to avoid need of the explicit declaration ?

import qualified Data.List as L

main = do
    print $ length $ L.nub [1,1,2,3]  -- 3, passed (Eq a, Num a) => [a]
    print $ length $ L.nub []         -- ambiguous type error, passed (Eq a) => [a]
    -- can help type system with explicit signature but how to avoid ?
    print $ length $ L.nub ([] :: [Int])

Surprisingly the same code written interactively in ghci has no issue with ambiguity and does prints zero length:

λ> :m +Data.List
λ> print $ length $ nub []
0  -- ?? can you explain ??

Update: It seems even same restricted as Data.List.nub length function won't stop complaints about ambiguous type:

length' :: Eq a => [a] -> Int
length' [] = 0
length' (x:xs) = 1 + length' xs

main = do
    print $ length' $ nub []
-- No instance for (Eq a0) arising from a use of ‘length'’
-- The type variable ‘a0’ is ambiguous
1
ghci turns on the extended default rules (-XExtendedDefaultRules) by default, see here, go to section 2.4.8.n. 1.8e9-where's-my-share m.
Thanks. Is that a hidden flag as ghci won't list it with :show language I get base language is: Haskell2010 with the following modifiers: -XNoDatatypeContexts -XNondecreasingIndentation ?David Unric
I think the rules are on only for interactive input and not for loaded modules, so the real compiler flag is not "really" on.n. 1.8e9-where's-my-share m.

1 Answers

10
votes

The problem is that [] has the polymorphic type (Eq a) => [a]. Since length doesn't add any particular constraint.

Specifically the type of length is:

length :: [a] -> Int

which is even more permissive than nub:

nub :: Eq a => [a] -> [a]

The compiler needs to use a specific instance of length there and is not able to deduce a type for a.

Now you have two options:

  1. Turn on the ExtendedDefaultRules extension with {-# LANGUAGE ExtendedDefaultRules #-} at the beginning of the file.
  2. Be explicit: ... L.nub ([] :: [Int])

I'd recommend the 2nd one by default, unless you fully understand the consequences of the first one.