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:
- GHC type defaulting: Haskell Report section
4.3.4 - GHCi’s extended type defaulting: Using GHCi section
2.4.5 - Monomorphic Restriction: Haskell
wiki
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
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
:tto ask for an inferred type, no such restriction or defaulting is imposed. Sobecause
3is indeed overloaded: it is admitted by any numeric type. The defaulting rules chooseIntegeras the default numeric type, soBut now let’s switch off the MR.
Without the MR, the definition is just as polymorphic as it can be, just as overloaded as
3. Just checking…Note that the polymorphic
y = 3is being differently specialized in these uses, according to thefromIntegermethod supplied with the relevantNuminstance. That is,yis not associated with a particular representation of3, but rather a scheme for constructing representations of3. 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
‘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
‘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.