*The Gentle Introduction To Haskell*

back next

## 10` `Typing Pitfalls

This short section give an intuitive description of a few common
problems that novices run into using Haskell's type system.

### 10.1` `Let-Bound Polymorphism

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`.

### 10.2` `Numeric Overloading

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)

`
`

### 10.3` `The Monomorphism Restriction

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.

*The Gentle Introduction To Haskell*

back next