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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
%**<title>A Gentle Introduction to Haskell: Numbers</title>
%**~header
\section{Numbers}
Haskell provides a rich collection of numeric types, based on those of
Scheme~\cite{RRRRS}, which in turn are based on Common
Lisp~\cite{steele:common-lisp}. (Those languages, however, are
dynamically typed.) The standard types include fixed- and
arbitrary-precision integers, ratios (rational numbers) formed from
each integer type, and single- and double-precision real and complex
floating-point. We outline here the basic characteristics of the
numeric type class structure and refer the reader to
\see{numbers} for details.
\subsection{Numeric Class Structure}
The numeric type classes (class @Num@ and those that lie below it)
account for
many of the standard Haskell classes. We also note that @Num@
is a subclass of @Eq@, but not of @Ord@; this is because the order
predicates do not apply to complex numbers. The subclass @Real@
of @Num@, however, is a subclass of @Ord@ as well.
The @Num@ class provides several basic operations common to all
numeric types; these include, among others, addition, subtraction,
negation, multiplication, and absolute value:
\bprog
@
(+), (-), (*) :: (Num a) => a -> a -> a
negate, abs :: (Num a) => a -> a
@
\eprog
\syn{@negate@ is the function applied by Haskell's only prefix operator,
minus; we can't call it @(-)@, because that is the subtraction
function, so this name is provided instead. For example,
@-x*y@ is equivalent to @negate (x*y)@. (Prefix minus has the same
syntactic precedence as infix minus, which, of course, is lower
than that of multiplication.)}
Note that @Num@ does {\em not} provide a division operator; two
different kinds of division operators are provided in two non-overlapping
subclasses of @Num@:
The class @Integral@ provides whole-number division and remainder
operations. The
standard instances of @Integral@ are @Integer@ (unbounded or
mathematical integers, also known as ``bignums'') and @Int@
(bounded, machine integers, with a range equivalent to at least
29-bit signed binary). A particular Haskell implementation might
provide other integral types in addition to these. Note that
@Integral@ is a subclass of @Real@, rather than of @Num@ directly;
this means that there is no attempt to provide Gaussian integers.
All other numeric types fall in the class @Fractional@, which provides
the ordinary division operator @(/)@. The further subclass
@Floating@ contains trigonometric, logarithmic, and exponential functions.
The @RealFrac@ subclass of @Fractional@ and @Real@ provides a function
@properFraction@, which decomposes a number into its whole and
fractional parts, and a collection of functions that round to
integral values by differing rules:
\bprog
@
properFraction :: (Fractional a, Integral b) => a -> (b,a)
truncate, round,
floor, ceiling: :: (Fractional a, Integral b) => a -> b
@
\eprog
The @RealFloat@ subclass of @Floating@ and @RealFrac@ provides
some specialized functions for efficient access to the components
of a floating-point number, the {\em exponent} and {\em significand}.
The standard types @Float@ and @Double@ fall in class @RealFloat@.
\subsection{Constructed Numbers}
Of the standard numeric types, @Int@, @Integer@, @Float@, and @Double@
are primitive. The others are made from these by type constructors.
@Complex@ (found in the library @Complex@) is a type constructor that
makes a complex type in class @Floating@ from a @RealFloat@ type:
\bprog
@
data (RealFloat a) => Complex a = !a :+ !a deriving (Eq, Text)
@
\eprog
The @!@ symbols are strictness flags; these were discussed in Section~
\ref{tut-strict-flag}.
Notice the context @RealFloat a@, which restricts the argument
type; thus, the standard complex types are @Complex Float@ and
@Complex Double@. We can also see from the @data@ declaration
that a complex number is written "x" @:+@ "y"; the arguments are
the cartesian real and imaginary parts, respectively. Since @:+@
is a data constructor, we can use it in pattern matching:
\bprog
@
conjugate :: (RealFloat a) => Complex a -> Complex a
conjugate (x:+y) = x :+ (-y)
@
\eprog
Similarly, the type constructor @Ratio@ (found in the @Rational@
library) makes a rational type in class @RealFrac@ from an instance of
@Integral@.
(@Rational@ is a type synonym for @Ratio Integer@.)
@Ratio@, however, is an abstract type constructor.
Instead of a data constructor like @:+@, rationals use the `@%@' function to
form a ratio from two integers. Instead of pattern matching,
component extraction functions are provided:
\bprog
@
(%) :: (Integral a) => a -> a -> Ratio a
numerator, denominator :: (Integral a) => Ratio a -> a
@
\eprog
Why the difference? Complex numbers in cartesian form are
unique---there are no nontrivial identities involving @:+@. On the
other hand, ratios are not unique, but have a canonical (reduced) form
that the implementation of the abstract data type must maintain; it is
not necessarily the case, for instance, that @numerator (x%y)@ is
equal to @x@, although the real part of @x:+y@ is always @x@.
\subsection{Numeric Coercions and Overloaded Literals}
\label{tut-num-constants}
The Standard Prelude and libraries provide several overloaded functions
that serve as explicit coercions:
\bprog
@
fromInteger :: (Num a) => Integer -> a
fromRational :: (Fractional a) => Rational -> a
toInteger :: (Integral a) => a -> Integer
toRational :: (RealFrac a) => a -> Rational
fromIntegral :: (Integral a, Num b) => a -> b
fromRealFrac :: (RealFrac a, Fractional b) => a -> b
fromIntegral = fromInteger . toInteger
fromRealFrac = fromRational . toRational
@
\eprog
Two of these are implicitly used to provide overloaded numeric literals:
An integer numeral (without a decimal point) is actually equivalent to
an application of @fromInteger@ to the value of the numeral as an
@Integer@. Similarly, a floating numeral (with a decimal point) is
regarded as an application of @fromRational@ to the value of the
numeral as a @Rational@. Thus, @7@ has the type @(Num a) => a@,
and @7.3@ has the type @(Fractional a) => a@. This means that we
can use numeric literals in generic numeric functions, for example:
\bprog
@
halve :: (Fractional a) => a -> a
halve x = x * 0.5
@
\eprog
This rather indirect way of overloading numerals has the additional
advantage that the method of interpreting a numeral as a number
of a given type can be specified in an @Integral@ or @Fractional@
instance declaration (since @fromInteger@ and @fromRational@ are
operators of those classes, respectively). For example, the
@Num@ instance of @(RealFloat a) => Complex a@ contains this method:
\bprog
@
fromInteger x = fromInteger x :+ 0
@
\eprog
This says that a @Complex@ instance of @fromInteger@ is defined to
produce a complex number whose real part is supplied by an appropriate
@RealFloat@ instance of @fromInteger@. In this manner, even
user-defined numeric types (say, quaternions) can make use of
overloaded numerals.
As another example, recall our first definition of @inc@ from Section
\ref{tut-values-etc}:
\bprog
@
inc :: Integer -> Integer
inc n = n+1
@
\eprog
Ignoring the type signature, the most general type of @inc@ is
@(Num a) => a->a@. The explicit type signature is legal,
however, since it is {\em more specific} than the principal type (a
more general type signature would cause a static error). The type
signature has the effect of restricting @inc@'s type, and in this
case would cause something like @inc (1::Float)@ to be ill-typed.
\subsection{Default Numeric Types}
Consider the following function definition:
\bprog
@
rms :: (Floating a) => a -> a -> a
rms x y = sqrt ((x^2 + y^2) * 0.5)
@
\eprog
The exponentiation function @(^)@ (one of three different standard
exponentiation operators with different typings, see \S{6.8.5}) has
the type @(Num a, Integral b) => a -> b -> a@, and since @2@ has the
type @(Num a) => a@, the type of @x^2@ is @(Num a, Integral b) => a@.
This is a problem; there is no way to resolve the overloading
associated with the type variable @b@, since it is in the context, but
has otherwise vanished from the type expression. Essentially, the
programmer has specified that @x@ should be squared, but has not
specified whether it should be squared with an @Int@ or an @Integer@
value of two. Of course, we can fix this:
\bprog
@
rms x y = sqrt ((x ^ (2::Integer) + y ^ (2::Integer)) * 0.5)
@
\eprog
It's obvious that this sort of thing will soon grow tiresome, however.
In fact, this kind of overloading ambiguity is not restricted to
numbers:
\bprog
@
show (read "xyz")
@
\eprog
As what type is the string supposed to be read? This is
more serious than the exponentiation ambiguity, because there, any
@Integral@ instance will do, whereas here, very different behavior
can be expected depending on what instance of @Text@ is used to
resolve the ambiguity.
Because of the difference between the numeric and general cases of the
overloading ambiguity problem, Haskell provides a solution that is
restricted to numbers: Each module may contain a {\em default
declaration,} consisting of the keyword @default@ followed by a
parenthesized, comma-separated list of numeric monotypes (types with
no variables). When an ambiguous type variable is discovered (such as
@b@, above), if at least one of its classes is numeric and all of its
classes are standard, the default list is consulted, and the first
type from the list that will satisfy the context of the type variable
is used. For example, if the default declaration
@default (Int, Float)@ is in effect, the ambiguous exponent above will
be resolved as type @Int@. (See \see{default-decls} for more details.)
The ``default default'' is @(Integer, Double)@, but
@(Integer, Rational, Double)@ may also be appropriate. Very cautious
programmers may prefer @default ()@, which provides no defaults.
%**~footer
|