haskelltype-inferencemonomorphism-restriction

Why do 3 and x (which was assigned 3) have different inferred types in Haskell?


Type inference in Haskell has a bit of a learning curve (to say the least!). A good way to start learning it is with simple examples. So, the following is a bit of a "hello world" for type inference.

Consider the following example:

Prelude> :t 3
3 :: (Num t) => t
Prelude> let x = 3
Prelude> :t x
x :: Integer

The question is thus: Why do 3 and x have different types?

Link Summary:

Read the answers below for the full story; here's just a link summary:

  1. GHC type defaulting: Haskell Report section 4.3.4
  2. GHCi's extended type defaulting: Using GHCi section 2.4.5
  3. Monomorphic Restriction: Haskell wiki

Solution

  • There's another factor here, mentioned in some of the links which acfoltzer includes, but it might be worth making explicit here. You're encountering the effect of the monomorphism restriction. When you say

    let x = 5
    

    you make a top-level definition of a variable. The MR insists that such definitions, when otherwise unaccompanied by a type signature, should be specialized to a monomorphic value by choosing (hopefully) suitable default instances for the unresolved type variables. By contrast, when you use :t to ask for an inferred type, no such restriction or defaulting is imposed. So

    > :t 3
    3 :: (Num t) => t
    

    because 3 is indeed overloaded: it is admitted by any numeric type. The defaulting rules choose Integer as the default numeric type, so

    > let x = 3
    > :t x
    x :: Integer
    

    But now let's switch off the MR.

    > :set -XNoMonomorphismRestriction
    > let y = 3
    > :t y
    y :: (Num t) => t
    

    Without the MR, the definition is just as polymorphic as it can be, just as overloaded as 3. Just checking...

    > :t y * (2.5 :: Float)
    y * (2.5 :: Float) :: Float
    > :t y * (3 :: Int)
    y * (3 :: Int) :: Int
    

    Note that the polymorphic y = 3 is being differently specialized in these uses, according to the fromInteger method supplied with the relevant Num instance. That is, y is not associated with a particular representation of 3, but rather a scheme for constructing representations of 3. Naïvely compiled, that's a recipe for slow, which some people cite as a motivation for the MR.

    I'm (locally pretending to be) neutral on the debate about whether the monomorphism restriction is a lesser or greater evil. I always write type signatures for top-level definitions, so there is no ambiguity about what I'm trying to achieve and the MR is beside the point.

    When trying to learn how the type system works, it's really useful to separate the aspects of type inference which

    1. ‘follow the plan’, specializing polymorphic definitions to particular use cases: a fairly robust matter of constraint-solving, requiring basic unification and instance resolution by backchaining; and

    2. ‘guess the plan’, generalizing types to assign a polymorphic type scheme to a definition with no type signature: that's quite fragile, and the more you move past the basic Hindley-Milner discipline, with type classes, with higher-rank polymorphism, with GADTs, the stranger things become.

    It's good to learn how the first works, and to understand why the second is difficult. Much of the weirdness in type inference is associated with the second, and with heuristics like the monomorphism restriction trying to deliver useful default behaviour in the face of ambiguity.