1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
<title>A Gentle Introduction to Haskell: Typing Pitfalls</title>
<body bgcolor="#ffffff"><i>A Gentle Introduction to Haskell, Version 1.4</i><br><a href="modules.html">back</a> <a href="arrays.html">next</a> <a href="index.html">top</a><hr>
<p>
<a name="sect10"></a>
<h2>10<tt> </tt>Typing Pitfalls</h2><p>
This short section give an intuitive description of a few common
problems that novices run into using Haskell's type system.<p>
<a name="sect10.1"></a>
<h3>10.1<tt> </tt>Let-Bound Polymorphism</h3><p>
Any language using the Hindley-Milner type system has what is called
<I>let-bound polymorphism</I>, because identifiers not bound using a
<tt>let</tt> or <tt>where</tt> clause (or at the top level of a module) are limited
with respect to their polymorphism. In particular, a
<I>lambda-bound</I> function (i.e., one passed as argument to another
function) cannot be instantiated in two different ways. For example,
this program is illegal:
<tt><br>
<br>
let f g = (g [], g 'a') -- ill-typed expression<br>
in f (\x->x)<br>
<br>
</tt>because <tt>g</tt>, bound to a lambda abstraction whose principal type is
<tt>a->a</tt>, is used within <tt>f</tt> in two different ways: once with type
<tt>[a]->[a]</tt>, and once with type <tt>Char->Char</tt>.<p>
<a name="sect10.2"></a>
<h3>10.2<tt> </tt>Numeric Overloading</h3><p>
It is easy to forget at times that numerals are <I>overloaded,</I> and
<I>not implicitly coerced</I> 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:
<tt><br>
<br>
average xs = sum xs / length xs -- Wrong!<br>
<br>
(/)</tt> requires fractional arguments, but <tt>length</tt>'s result is
an <tt>Int</tt>. The type mismatch must be corrected with an explicit coercion:
<tt><br>
<br>
average :: (Fractional a) => [a] -> a<br>
average xs = sum xs / fromIntegral (length xs)<br>
<br>
<p>
</tt><a name="sect10.3"></a>
<h3>10.3<tt> </tt>The Monomorphism Restriction</h3><p>
The Haskell type system contains a restriction related to type classes
that is not found in ordinary Hindley-Milner type systems: the
<I>monomorphism restriction</I>. The reason for this restriction is related
to a subtle type ambiguity and is explained in full detail in the
Report (<a href="../report/decls.html#sect:monomorphism-restriction">§4.5.5</a>). A simpler explanation
follows:<p>
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 <I>monomorphic</I>. 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.<p>
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 <I>any</I> type signature will do (as long it is
type correct).<p>
A common violation of the restriction happens with functions defined
in a higher-order manner, as in this definition of <tt>sum</tt> from the
Standard Prelude:
<tt><br>
<br>
sum = foldl (+) 0<br>
<br>
</tt>As is, this would cause a static type error. We can fix the problem
by adding the type signature:
<tt><br>
<br>
sum :: (Num a) => [a] -> a<br>
<br>
</tt>Also note that this problem would not have arisen if we had written:
<tt><br>
<br>
sum xs = foldl (+) 0 xs<br>
<br>
</tt>because the restriction only applies to pattern bindings.<p>
<hr><body bgcolor="#ffffff"><i>A Gentle Introduction to Haskell, Version 1.4</i><br><a href="modules.html">back</a> <a href="arrays.html">next</a> <a href="index.html">top</a>
<p>
|