Search code examples
haskelltypesghcitype-systems

Why such different behaviour with `Ambiguous type..` error (in ghci)?


This example works with ghci, load this file:

import Safe

t1 = tailMay []

and put in ghci:

> print t1
Nothing

But if we add analogous definition to previous file, it doesn't work:

import Safe

t1 = tailMay []
t2 = print $ tailMay []

with such error:

    * Ambiguous type variable `a0' arising from a use of `print'
      prevents the constraint `(Show a0)' from being solved.
      Probable fix: use a type annotation to specify what `a0' should be.
      These potential instances exist:
        instance Show Ordering -- Defined in `GHC.Show'
        instance Show Integer -- Defined in `GHC.Show'
        instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
        ...plus 22 others

That is 3rd sample for ghc with the same error:

import Safe

t1 = tailMay

main = do
  print $ t1 []
  print $ t1 [1,2,3]

Why? And how to fix the second sample without explicit type annotation?


Solution

  • The issue here is that tailMay [] can generate an output of type Maybe [a] for any a, while print can take an input of type Maybe [a] for any a (in class Show).

    When you compose a "universal producer" and a "universal consumer", the compiler has no idea about which type a to pick -- that could be any type in class Show. The choice of a could matter since, in principle, print (Nothing :: Maybe [Int]) could print something different from print (Nothing :: Maybe [Bool]). In this case, the printed output would be the same, but only because we are lucky.

    For instance print ([] :: [Int]) and print ([] :: [Char]) will print different messages, so print [] is ambiguous. Hence, GHC reject it, and requires an explicit type annotation (or a type application @ type, using an extension).

    Why, then, such ambiguity is accepted in GHCi? Well, GHCi is meant to be used for quick experiments, and as such, as a convenience feature, it will try hard to default these ambiguous a. This is done using the extended defaulting rules, which could (I guess) in principle be turned on in GHC as well by turning on that extension. This is, however, not recommended since sometimes the defaulting rule can choose some unintended type, making the code compile but with an unwanted runtime behavior.

    The common solution to this issue is using an annotation (or @ type), because it provides more control to the programmer, makes the code easier to read, and avoids surprises.