# Haskell/Type basics II

So far, we have shrewdly avoided number types in our examples. In one exercise, we even went as far as asking you to "pretend" the arguments to `(+)` had to be of type `Int`. So, from what are we hiding?

In this chapter, we will show how numerical types are handled in Haskell. While doing so, we will introduce some important features of the type system. Before diving into the text, though, pause for a moment and consider the following question: what should be the type of the function `(+)`?^{[1]}

## The `Num` class[edit]

As far as everyday mathematics is concerned, there are very few restrictions on which kind of numbers we can add together. (two natural numbers), (a negative integer and a rational number), (a rational and an irrational)... all of these are valid – indeed, any two real numbers can be added together. In order to capture such generality in the simplest way possible we would like to have a very general `Number` type in Haskell, so that the signature of `(+)` would be simply

(+) :: Number -> Number -> Number

That design, however, does not fit well with the way computers perform arithmetic. While integer numbers in programs can be quite straightforwardly handled as sequences of binary digits in memory, that approach does not work for non-integer real numbers,^{[2]} thus making it necessary for a more involved encoding to support them: floating point numbers. While floating point provides a reasonable way to deal with real numbers in general, it has some inconveniences (most notably, loss of precision) which make using the simpler encoding worthwhile for integer values. We are thus left with at least two different ways of storing numbers, one for integers and another one for general real numbers, which should correspond to different Haskell types. Furthermore, computers are only able to perform operations like `(+)` on a pair of numbers if they are in the same format. That should put an end to our hopes of using a universal `Number` type – or even having `(+)` working with both integers and floating-point numbers...

It is easy, however, to see reality is not that bad. We *can* use `(+)` with both integers and floating point numbers:

Prelude>3 + 4 7 Prelude>4.34 + 3.12 7.46

When discussing lists and tuples, we saw that functions can accept arguments of different types if they are made *polymorphic*. In that spirit, one possible type signature for `(+)` that would account for the facts above would be:

(+) :: a -> a -> a

`(+)` would then take two arguments of the same type `a` (which could be integers or floating-point numbers) and evaluate to a result of type `a`. There is a problem with that solution, however. As we saw before, the type variable `a` can stand for *any* type at all. If `(+)` had that type signature we would be able to `(+)` two `Bool`, or two `Char`, which would make very little sense. Rather, the actual type signature of `(+)` takes advantage of a language feature that allows us to express the semantic restriction that `a` can be any type *as long as it is a number type*:

(+) :: (Num a) => a -> a -> a

`Num` is a **typeclass** - a group of types which includes all types which are regarded as numbers^{[3]}. The `(Num a) =>` part of the signature restricts `a` to number types – or, more accurately, *instances* of `Num`.

## Numeric types[edit]

But what are the *actual* number types – the instances of `Num` that `a` stands for in the signature? The most important numeric types are `Int`, `Integer` and `Double`:

`Int`corresponds to the vanilla integer type found in most languages. It has fixed maximum and minimum values that depend on a computer's processor. (In 32-bit machines the range goes from -2147483648 to 2147483647).

`Integer`also is used for integer numbers, but unlike`Int`it supports arbitrarily large values – at the cost of some efficiency.

`Double`is the double-precision floating point type, a good choice for real numbers in the vast majority of cases. (There is also`Float`, the single-precision counterpart of`Double`, which is usually less attractive due to further loss of precision.)

These types are available by default in Haskell and are the ones you will generally deal with in everyday tasks.

### Polymorphic guesswork[edit]

There is one thing we haven't explained yet. If you tried the examples of addition we mentioned at the beginning you know that something like this is perfectly valid:

Prelude> (-7) + 5.12 -1.88

Here, it seems we are adding two numbers of different types – an integer and a non-integer. Shouldn't the type of `(+)` make that impossible?

To answer that question we have to see what the types of the numbers we entered actually are:

Prelude> :t (-7) (-7) :: (Num a) => a

And, lo and behold, `(-7)` is neither `Int` nor `Integer`! Rather, it is a *polymorphic constant*, which can "morph" into any number type if need be. The reason for that becomes clearer when we look at the other number...

Prelude> :t 5.12 5.12 :: (Fractional t) => t

`5.12` is also a polymorphic constant, but one of the `Fractional` class, which is more restrictive than `Num` – every `Fractional` is a `Num`, but not every `Num` is a `Fractional` (for instance, `Int`s and `Integer`s are not `Fractional`).

When a Haskell program evaluates `(-7) + 5.12`, it must settle for an actual type for the numbers. It does so by performing type inference while accounting for the class specifications. `(-7)` can be any `Num`, but there are extra restrictions for `5.12`, so its type will define what `(-7)` will become. Since there is no other clues to what the types should be, `5.12` will assume the default `Fractional` type, which is `Double`; and, consequently, `(-7)` will become a `Double` as well, allowing the addition to proceed normally and return a `Double`^{[4]}.

There is a nice quick test you can do to get a better feel of that process. In a source file, define

x = 2

then load the file in GHCi and check the type of `x`. Then, change the file to add a `y` variable,

x = 2 y = x + 3

reload it and check the types of `x` and `y`. Finally, modify `y` to

x = 2 y = x + 3.1

and see what happens with the types of both variables.

### Monomorphic trouble[edit]

The sophistication of the numerical types and classes occasionally leads to some complications. Consider, for instance, the common division operator `(/)`. It has the following type signature:

(/) :: (Fractional a) => a -> a -> a

Restricting `a` to fractional types is a must because the division of two integer numbers will often not result in an integer. Nevertheless, we can still write something like

Prelude> 4 / 3 1.3333333333333333

because the literals `4` and `3` are polymorphic constants and therefore assume the type `Double` at the behest of `(/)`. Suppose, however, we want to divide a number by the length of a list^{[5]}. The obvious thing to do would be using the `length` function:

Prelude> 4 / length [1,2,3]

Unfortunately, that blows up:

<interactive>:1:0: No instance for (Fractional Int) arising from a use of `/' at <interactive>:1:0-17 Possible fix: add an instance declaration for (Fractional Int) In the expression: 4 / length [1, 2, 3] In the definition of `it': it = 4 / length [1, 2, 3]

As usual, the problem can be understood by looking at the type signature of `length`:

length :: [a] -> Int

The result of `length` is not a polymorphic constant, but an `Int`; and since an `Int` is not a `Fractional` it can't fit the signature of `(/)`.

There is a handy function which provides a way of escaping from this problem. Before following on with the text, try to guess what it does only from the name and signature:

fromIntegral :: (Integral a, Num b) => a -> b

`fromIntegral` takes an argument of some `Integral` type (like `Int` or `Integer`) and makes it a polymorphic constant. By combining it with `length` we can make the length of the list fit into the signature of `(/)`:

Prelude> 4 / fromIntegral (length [1,2,3]) 1.3333333333333333

While this expression may look overly complex at first, this approach makes it easier to be rigorous when manipulating numbers. If you define a function with an `Int` argument, it will never be converted to an `Integer` or `Double`, unless you explicitly tell the program to do so by using a function like `fromIntegral`. As a direct consequence of its refined type system, there is a surprising diversity of classes and functions dealing with numbers in Haskell.

## Classes beyond numbers[edit]

There are many other use cases for typeclasses beyond arithmetic. For example, the type signature of `(==)` is:

(==) :: (Eq a) => a -> a -> Bool

Like `(+)` or `(/)`, `(==)` is a polymorphic function. It compares two values of the same type, which must belong to the class `Eq` and returns a `Bool`. `Eq` is simply the class of types of values which can be compared for equality, and includes all of the basic non-functional types.^{[6]}

Typeclasses are a very general language feature which adds a lot to the power of the type system. Later in the book we will return to this topic to see how to use them in custom ways.

## Notes[edit]

- ↑ If you followed our recommendations in "Type basics", chances are you have already seen the rather exotic answer by testing with
`:t`... if that is the case, consider the following analysis as a path to understanding the meaning of that signature. - ↑ One of the reasons being that between any two real numbers there are infinitely many real numbers – and that can't be directly mapped into a representation in memory no matter what we do.
- ↑ That is a very loose definition, but will suffice until we are ready to discuss typeclasses in more detail.
- ↑
*For seasoned programmers:*This appears to have the same effect that programs in C (and many other languages) manage with an*implicit cast*– by which an integer literal is silently converted to a double. The difference is that in C, the conversion is done behind your back, while in Haskell it only occurs if the variable/literal is a polymorphic constant. The difference will become clearer shortly, when we show a counter-example. - ↑ A reasonable scenario – think of computing an average of the values in a list.
- ↑ Comparing two functions for equality is considered to be intractable