This short section give an intuitive description of a few common problems that novices run into using Haskell's type system.
Any language using the Hindley-Milner type system has what is called
let-bound polymorphism, because identifiers not bound using a
let or where clause (or at the top level of a module) are limited
with respect to their polymorphism. In particular, a
lambda-bound function (i.e., one passed as argument to another
function) cannot be instantiated in two different ways. For example,
this program is illegal:
let f g = (g [], g 'a') -- ill-typed expression
in f (\x->x)
because g, bound to a lambda abstraction whose principal type is
a->a, is used within f in two different ways: once with type
[a]->[a], and once with type Char->Char.
It is easy to forget at times that numerals are overloaded, and
not implicitly coerced to the various numeric types, as in many
other languages. More general numeric expressions sometimes cannot be
quite so generic. A common numeric typing error is something like the
following:
average xs = sum xs / length xs -- Wrong!
(/) requires fractional arguments, but length's result is
an Int. The type mismatch must be corrected with an explicit coercion:
average :: (Fractional a) => [a] -> a
average xs = sum xs / fromIntegral (length xs)
The Haskell type system contains a restriction related to type classes that is not found in ordinary Hindley-Milner type systems: the monomorphism restriction. The reason for this restriction is related to a subtle type ambiguity and is explained in full detail in the Report (ยง4.5.5). A simpler explanation follows:
The monomorphism restriction says that any identifier bound by a pattern binding (which includes bindings to a single identifier), and having no explicit type signature, must be monomorphic. An identifier is monomorphic if is either not overloaded, or is overloaded but is used in at most one specific overloading and is not exported.
Violations of this restriction result in a static type error. The simplest way to avoid the problem is to provide an explicit type signature. Note that any type signature will do (as long it is type correct).
A common violation of the restriction happens with functions defined
in a higher-order manner, as in this definition of sum from the
Standard Prelude:
sum = foldl (+) 0
As is, this would cause a static type error. We can fix the problem
by adding the type signature:
sum :: (Num a) => [a] -> a
Also note that this problem would not have arisen if we had written:
sum xs = foldl (+) 0 xs
because the restriction only applies to pattern bindings.