haskelltypesghcmonomorphism-restriction

Specific type inference using uncurry function


I've been playing with the uncurry function in GHCi and I've found something I couldn't quite get at all. When I apply uncurry to the (+) function and bind that to some variable like in the code below, the compiler infers its type to be specific to Integer:

Prelude> let add = uncurry (+)
Prelude> :t add
add :: (Integer, Integer) -> Integer

However, when ask for the type of the following expression I get (what I expect to be) the correct result:

Prelude> :t uncurry (+)
uncurry (+) :: (Num a) => (a, a) -> a

What would cause that? Is it particular to GHCi?

The same applies to let add' = (+).

NOTE: I could not reproduce that using a compiled file.


Solution

  • This has nothing to do with ghci. This is the monomorphism restriction being irritating. If you try to compile the following file:

    add = uncurry (+)
    main = do
        print $ add (1,2 :: Int)
        print $ add (1,2 :: Double)
    

    You will get an error. If you expand:

    main = do
        print $ uncurry (+) (1,2 :: Int)
        print $ uncurry (+) (1,2 :: Double)
    

    Everything is fine, as expected. The monomorphism restriction refuses to make something that "looks like a value" (i.e. defined with no arguments on the left-hand side of the equals) typeclass polymorphic, because that would defeat the caching that would normally occur. Eg.

    foo :: Integer
    foo = expensive computation
    
    bar :: (Num a) => a
    bar = expensive computation
    

    foo is guaranteed only to be computed once (well, in GHC at least), whereas bar will be computed every time it is mentioned. The monomorphism restriction seeks to save you from the latter case by defaulting to the former when it looks like that's what you wanted.

    If you only use the function once (or always at the same type), type inference will take care of inferring the right type for you. In that case ghci is doing something slightly different by guessing sooner. But using it at two different types shows what is going on.

    When in doubt, use a type signature (or turn off the wretched thing with {-# LANGUAGE NoMonomorphismRestriction #-}).