O'Reilly logo

Real World Haskell by Donald Bruce Stewart, Bryan O'Sullivan, John Goerzen

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

First Steps with Types

While we’ve talked a little about types already, our interactions with ghci have so far been free of much type-related thinking. We haven’t told ghci what types we’ve been using, and it’s mostly been willing to accept our input.

Haskell requires type names to start with an uppercase letter, and variable names must start with a lowercase letter. Bear this in mind as you read on; it makes it much easier to follow the names.

The first thing we can do to start exploring the world of types is to get ghci to tell us more about what it’s doing. ghci has a command, :set, that lets us change a few of its default behaviors. We can tell it to print more type information as follows:

ghci> :set +t
ghci> 'c'
'c'
it :: Char
ghci> "foo"
"foo"
it :: [Char]

What the +t does is tell ghci to print the type of an expression after the expression. That cryptic it in the output can be very useful: it’s actually the name of a special variable, in which ghci stores the result of the last expression we evaluated. (This isn’t a Haskell language feature; it’s specific to ghci alone.) Let’s break down the meaning of the last line of ghci output:

  • It tells us about the special variable it.

  • We can read text of the form x :: y as meaning “the expression x has the type y.”

  • Here, the expression it has the type [Char]. (The name String is often used instead of [Char]. It is simply a synonym for [Char].)

Here are a few more of Haskell’s names for types, from expressions of the sort that we’ve already seen:

ghci> 7 ^ 80
40536215597144386832065866109016673800875222251012083746192454448001
it :: Integer

Haskell’s integer type is named Integer. The size of an Integer value is bounded only by your system’s memory capacity.

Rational numbers don’t look quite the same as integers. To construct a rational number, we use the (%) operator. The numerator is on the left, the denominator on the right:

ghci> :m +Data.Ratio
ghci> 11 % 29
11%29
it :: Ratio Integer

For convenience, ghci lets us abbreviate many commands, so we can write :m instead of :module to load a module.

Notice there are two words on the righthand side of the :: in the preceding code. We can read this as a “Ratio of Integer.” We might guess that a Ratio must have values of type Integer as both numerator and denominator. Sure enough, if we try to construct a Ratio where the numerator and denominator are of different types or of the same nonintegral type, ghci complains:

ghci> 3.14 % 8

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Integral t' arising from a use of `%' at <interactive>:1:0-7
      `Fractional t'
        arising from the literal `3.14' at <interactive>:1:0-3
    Probable fix: add a type signature that fixes these type variable(s)
ghci> 1.2 % 3.4

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Integral t' arising from a use of `%' at <interactive>:1:0-8
      `Fractional t'
        arising from the literal `3.4' at <interactive>:1:6-8
    Probable fix: add a type signature that fixes these type variable(s)

Although it is initially useful to have :set +t giving us type information for every expression we enter, this is a facility we will quickly outgrow. After a while, we will often know what type we expect an expression to have. We can turn off the extra type information at any time, using the :unset command:

ghci> :unset +t
ghci> 2
2

Even with this facility turned off, we can still get that type information easily when we need it, using another ghci command:

ghci> :type 'a'
'a' :: Char
ghci> "foo"
"foo"
ghci> :type it
it :: [Char]

The :type command will print type information for any expression we give it (including it, as we see here). It won’t actually evaluate the expression; it checks only its type and prints that.

Why are the types reported for these two expressions different?

ghci> 3 + 2
5
ghci> :type it
it :: Integer
ghci> :type 3 + 2
3 + 2 :: (Num t) => t

Haskell has several numeric types. For example, a literal number such as 1 could, depending on the context in which it appears, be an integer or a floating-point value. When we force ghci to evaluate the expression 3 + 2, it has to choose a type so that it can print the value, and it defaults to Integer. In the second case, we ask ghci to print the type of the expression without actually evaluating it, so it does not have to be so specific. It answers, in effect, “its type is numeric.” We will see more of this style of type annotation in Chapter 6.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required