Here's my number
Numbers in Haskell are complicated because in the Haskell world there are many different kinds of number…
— Richard Bird, 20151
Haskell represents a number in one of several ways, depending on the kind of number you are dealing with. Is the number an integer? Haskell has the types Int
and Integer
to represent an integer. Is the number a floating-point number? Use the types Float
and Double
to represent floating-point numbers. In this section, we consider how Haskell represents and parses integers. The discussion on floating-point numbers is deferred to the section Does it sink or float?.
The type Int
Haskell represents integers via the types Int
and Integer
. The type Int
represents a fixed-precision integer. An integer represented as the type Int
has a minimum value at least $-2^{29}$ and a maximum value at least $2^{29} - 1$. The exact range depends on the implementation. Use minBound and maxBound to check the exact range for your platform. On my computer, the exact range of Int
is given as:
1
2
3
4
5
ghci> minBound :: Int
-9223372036854775808
ghci> maxBound :: Int
9223372036854775807
ghci>
What is that double colon symbol ::
in the above GHCi session? In Haskell, the symbol ::
can be read as “has type”. The symbol ::
is Haskell’s way of implementing type annotation, a way to associate an expression with a type. In the above GHCi session, the line minBound :: Int
can be read as, “minBound
has type Int
”.
Polymorphism
The method minBound
is polymorphic. The specific version of minBound
that is executed depends on the type. The method minBound
belongs to the class Bounded
. A class derived from Bounded
should have its own implementation of minBound
. The type Int
happens to be derived from Bounded
and has its own implementation of minBound
. When you call minBound
without any type annotation, the compiler (or GHCi) might find it difficult to infer the type information from context alone. However, annotating minBound
with the type Int
helps the compiler (or GHCi) to execute the version of minBound
implemented for Int
.
Integer overflow
Since Int
represents fixed-precision integers, there is always the possibility of integer overflow. The following GHCi session demonstrates the issue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ghci> k = minBound :: Int
ghci> n = maxBound :: Int
ghci> :type k
k :: Int
ghci> :type n
n :: Int
ghci> k
-9223372036854775808
ghci> k - 1
9223372036854775807
ghci> n
9223372036854775807
ghci> n + 1
-9223372036854775808
The GHCi command :type
allows you to obtain information about the type of an expression. We used the command to confirm that the variables k
and n
both have the type Int
.
Arithmetic operations
The above GHCi session also shows that Int
supports integer addition and subtraction. What about integer multiplication?
1
2
3
4
5
6
7
ghci> a = 4 :: Int
ghci> b = 2 :: Int
ghci> c = a * b
ghci> :type c
c :: Int
ghci> c
8
So far so good. What about integer division? We know that $4 / 2 = 2$. Let’s use Haskell to confirm our result.
1
2
3
4
5
6
7
8
ghci> a = 4 :: Int
ghci> b = 2 :: Int
ghci> a / b
<interactive>:3:3: error:
* No instance for (Fractional Int) arising from a use of '/'
* In the expression: a / b
In an equation for 'it': it = a / b
What is going on here? The type Int
does not support integer division? In fact, the type Int
does support integer division. You just need to use the right method for the job. The operator /
is a method implemented for data of type Fractional
, not data of type Int
. Observe the next GHCi session:
1
2
ghci> :type (/)
(/) :: Fractional a => a -> a -> a
The part (/) :: Fractional
means that /
is defined for data of type Fractional
. Do not worry about the rest of the above line. We will discuss it in the section Prefix notation.
You must use the method div
for integer division because that method would give you the quotient when dividing an integer by another integer. Alternatively, use the method quot
to make it clear that your intention is to obtain the quotient when dividing one integer by another integer. Observe the following GHCi session.
1
2
3
4
5
6
7
8
9
ghci> a = 5 :: Int
ghci> b = 4 :: Int
ghci> c = 2 :: Int
ghci> div a c
2
ghci> div b c
2
ghci> quot a b
1
Prefix notation
The latter GHCi session brings up one issue. Why did we write the code div a c
to divide a
by c
? Functions and methods in Haskell are called using prefix notation. In languages such as C, JavaScript, or Python if you define a function named div
that takes two arguments a
and c
, you would use the function like so: div(a, c)
. This is prefix notation. Haskell uses prefix notation, but dispenses with the parentheses. The Haskell code div a c
means: apply the method (or function) div
to the arguments a
and c
. Let’s use the GHCi command :type
to show the signature (or declaration) of div
.
1
2
ghci> :type div
div :: Integral a => a -> a -> a
We have seen the symbol =>
before in the section Arithmetic operations. The symbol =>
separates two parts of the signature of div
. Everything to the left of =>
, excluding the segment div ::
, is a constraint on the type of data on which div
can operate. The segment Integral a
means that div
can work with any data a
that has type Integral
. The type Int
happens to be based on Integral
, as can be seen in the GHCi session below. Search for the line instance Integral Int
.
1
2
3
4
5
6
7
8
9
10
11
12
13
ghci> :info Int
type Int :: *
data Int = GHC.Types.I# GHC.Prim.Int#
-- Defined in ‘GHC.Types’
instance Bounded Int -- Defined in ‘GHC.Enum’
instance Read Int -- Defined in ‘GHC.Read’
instance Enum Int -- Defined in ‘GHC.Enum’
instance Integral Int -- Defined in ‘GHC.Real’
instance Num Int -- Defined in ‘GHC.Num’
instance Real Int -- Defined in ‘GHC.Real’
instance Show Int -- Defined in ‘GHC.Show’
instance Eq Int -- Defined in ‘GHC.Classes’
instance Ord Int -- Defined in ‘GHC.Classes’
The information from the above two GHCi sessions says that you can use div
for integer division when the number is of type Int
. Phew! So many words to express a simple idea.
What about everything to the right of the symbol =>
? The right-hand side of =>
tells you two pieces of information.
- First,
div
takes two parameters of the same type. That is what the first twoa
s ina -> a -> a
means. - Second,
div
returns a result of the same type as its parameters. That is what the last (or rightmost)a
means ina -> a -> a
.
Infix notation
The common arithmetic operators such as +
(addition), -
(subtraction), /
(division), and *
(multiplication) can be used by means of infix notation. Take for example the operator +
. This operator requires two arguments. You write its first argument, followed by the operator +
, and finally write the second argument. We have seen infix notation already in the section Arithmetic operations. Refer to the following GHCi session to refresh your memory.
1
2
3
4
5
6
7
8
9
10
ghci> a = 6 :: Int
ghci> b = 2 :: Int
ghci> a + b
8
ghci> a - b
4
ghci> a * b
12
ghci> a `div` b
3
Hang on. Why is the method div
being used via infix notation? More importantly, how did we call div
(a prefix method) via infix notation? In general, a prefix method or function can be called via infix notation. All you have to do is use the backtick character, i.e. `, to delimit the method or function name. The method quot
can also be used via infix notation like so:
1
2
3
4
5
6
ghci> a = 6 :: Int
ghci> b = 4 :: Int
ghci> quot a b
1
ghci> a `quot` b
1
A method or function that is normally called via prefix notation can be called via infix notation. What about the other way around? If an operator is normally used via infix notation, how can we use it via prefix notation? We surround the operator with parentheses, as shown in the next GHCi session.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ghci> a = 7 :: Int
ghci> b = 3 :: Int
ghci> a + b
10
ghci> (+) a b
10
ghci> a - b
4
ghci> (-) a b
4
ghci> a * b
21
ghci> (*) a b
21
The type Integer
Daniel Jackson: Sorry, you’re not my type, and I’m more than a little disturbed to think I might be yours.
— Stargate SG-1, season 8, episode 12 “Prometheus Unbound”, 2005
In contrast to Int
, the type Integer
represents arbitrary precision integers. Your integer can be as large or small as you want, subject to the available RAM of your computer. The following GHCi session demonstrates that Integer
can represent integers much larger (or smaller) than those representable by Int
. The caret symbol ^
means exponentiation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ghci> minBound :: Int
-9223372036854775808
ghci> maxBound :: Int
9223372036854775807
ghci> k = -9223372036854775808 :: Integer
ghci> n = 9223372036854775807 :: Integer
ghci> k - n
-18446744073709551615
ghci> k * n
-85070591730234615856620279821087277056
ghci> n + n
18446744073709551614
ghci> n^3
784637716923335095224261902710254454442933591094742482943
Like Int
, the division operator /
does work with data of type Integer
. You can use the methods div
or quot
for integer division on data of type Integer
because Integer
is based on the type Integral
. Here’s the relevant information from GHCi. The line instance Integral Integer
means that Integer
is based on Integral
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ghci> :info Integer
type Integer :: *
data Integer
= GHC.Num.Integer.IS GHC.Prim.Int#
| GHC.Num.Integer.IP GHC.Prim.ByteArray#
| GHC.Num.Integer.IN GHC.Prim.ByteArray#
-- Defined in ‘GHC.Num.Integer’
instance Read Integer -- Defined in ‘GHC.Read’
instance Enum Integer -- Defined in ‘GHC.Enum’
instance Integral Integer -- Defined in ‘GHC.Real’
instance Num Integer -- Defined in ‘GHC.Num’
instance Real Integer -- Defined in ‘GHC.Real’
instance Show Integer -- Defined in ‘GHC.Show’
instance Eq Integer -- Defined in ‘GHC.Num.Integer’
instance Ord Integer -- Defined in ‘GHC.Num.Integer’
ghci> :type div
div :: Integral a => a -> a -> a
ghci> :type quot
quot :: Integral a => a -> a -> a
Syntactic sugar
Child: [Playing in the rain.]
Mother: Come inside, sweetie. Sugar melts in the rain.
Here is a simple question. How would you negate a number? One way is to multiply the number by $-1$. If the number appears within a mathematical expression, you could prefix the number with the minus sign -
. Some programming languages allow you to prefix a number with the symbol -
to indicate negation. Haskell allows you to do so as well.
1
2
ghci> -2
-2
So far so good. The expression $3 \times -2$ evaluates to $-6$. Python gives the result:
1
2
>>> 3 * -2
-6
JavaScript produces:
1
console.log(3 * -2); //-> -6
And finally, here is Ruby:
1
puts 3 * -2 #-> -6
Haskell complains when we enter the same expression into GHCi.
1
2
3
4
5
ghci> 3 * -2
<interactive>:1:1: error:
Precedence parsing error
cannot mix '*' [infixl 7] and prefix `-' [infixl 6] in the same infix expression
However, the expression $-2 \times 3$ produces the correct result.
1
2
ghci> -2 * 3
-6
The reason is that, in Haskell, -2
is syntactic sugar for negate 2
. We have seen syntactic sugar before in the section Infix notation, where we discussed backticks as a way to use a method or function in infix notation. Recall that integer division can be written as:
1
2
3
4
5
6
ghci> a = 8 :: Integer
ghci> b = 3 :: Integer
ghci> div a b -- prefix notation
2
ghci> a `div` b -- infix notation
2
Haskell treats
1
a `div` b
as syntactic sugar for div a b
.
As its name implies, the method negate
produces the negation of its argument. In effect, the method takes a number and multiplies its argument by $-1$. For Haskell to evaluate the expression $3 \times -2$ to $-6$, we must surround $-2$ within parentheses like so: $3 \times (-2)$. The symbol -
in the expression $3 \times -2$ is parsed as the subtraction operator and Haskell assumes we are using the subtraction operator in infix notation. On the other hand, Haskell has no problem parsing expressions such as 3 * negate 2
or $-2 \times 3$. Both yield the same, correct result. Keep the above quirks in mind to prevent unexpected results in your programs or compilation errors.
Printing numbers
Let’s learn some facts about Tabby the cat. Tabby is currently one year old. The Haskell program below shows Tabby’s age after a given number of years.
1
2
3
4
5
6
-- | How old Tabby will be in x years.
main = do
let age = 1 :: Integer
let x = 3 :: Integer
putStrLn ("Tabby's age: " ++ show age)
putStrLn ("In 3 years time, Tabby will be: " ++ show (age + x))
Running the program shows:
1
2
3
4
5
6
$ ghc age.hs
[1 of 2] Compiling Main ( age.hs, age.o )
[2 of 2] Linking age
$ ./age
Tabby's age: 1
In 3 years time, Tabby will be: 4
Do let us show you
The program age.hs
presents a few new concepts. Let’s tackle each idea in turn:
- The keyword
do
. This keyword allows you to string a sequence of actions together, one action per line. Indentation is important2 and allows the Haskell compiler to figure out whether a line of code is within, or outside of, thedo
block. The keyworddo
is syntactic sugar that allows you to write clean and readable Haskell code, in a manner similar to how you would define a function in a procedural language such as C, JavaScript, or Python. - The keyword
let
. Normally, within a GHCi session you would assign a value to a variable like so:num = 42
. Inside ado
block of a script, you must prefix each assignment operation with the keywordlet
. Within ado
block, you would writelet num = 42
instead ofnum = 42
. - The operator
++
. This operator allows you to concatenate two strings.3 Do not confuse it with the increment operator from C and JavaScript. - The method
show
. This method converts a value to a string, provided that the value supports such a conversion. In the programage.hs
the variableage
has typeInteger
and cannot be concatenated with the string"Tabby's age: "
. We useshow
to convert the value ofage
to a string. The operator++
can then concatenate the strings on its left- and right-hand sides.
The combination of do
and let
allows you to mimic a procedural style of programming. Compare the script age.hs
with its counterpart in C:
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
/**
* How old Tabby will be in x years.
*/
int main() {
int age = 1;
int x = 3;
printf("Tabby's age: %d\n", age);
printf("In 3 years time, Tabby will be: %d\n", age + x);
return 0;
}
Here’s a version of age.hs
in JavaScript:
1
2
3
4
5
// How old Tabby will be in x years.
const age = 1;
const x = 3;
console.log(`Tabby's age: ${age}`);
console.log(`In 3 years time, Tabby will be: ${age + x}`);
Below is a Python version:
1
2
3
4
5
6
7
8
def main():
"""
How old Tabby will be in x years.
"""
age = 1
x = 3
print(f"Tabby's age: {age}")
print(f"In 3 years time, Tabby will be: {age + x}")
Data immutability
In functional programming, data should be immutable. Once you have assigned a value to a variable, you should not assign a different value to the same variable. Not only can you use let
to declare a variable and assign it a value, you can also use let
to reassign a different value to the same variable. However, doing so would be a violation of immutability. Try to follow the principle of immutability as much as possible. The following program, while being valid Haskell code, should not be how you write functional code.
1
2
3
4
5
-- | Use let to reassign values to a variable.
main = do
let a = 3 :: Int -- Not using this value
let a = 4 :: Int -- Use this new value
putStrLn (show a)
Exercises
Exercise 1. Load GHCi and type in minBound
(or maxBound
) at the prompt. What does GHCi show? Why do you think GHCi shows such output?
Exercise 2. Determine the exact range of Int
on your computer. An integer represented by the type Int
has a minimum and maximum values of at least $-2^{29}$ and $2^{29} - 1$, respectively. Use Haskell to obtain the actual digits in each of the latter two numbers.
Exercise 3. The GHCi command :type
shows the type information of an expression. A similar command is :info
. Read up on the latter command.
Exercise 4. Use :type
to show the signature of the method quot
. Explain what the signature of quot
means.
Exercise 5. Enter the code minBound :: Integer
at the prompt of GHCi. What does GHCi say? Why do you get that result? Repeat the exercise for the code maxBound :: Integer
.
Exercise 6. The methods div
and quot
both perform integer division, but their results can be different. Use each method to perform integer division with the following types of integers:
- Positive and positive.
- Positive and negative.
- Negative and positive.
- Negative and negative.
- Zero and positive (or negative).
- Positive (or negative) and zero.
Exercise 7. The method mod
returns the integer remainder when one integer is divided by another integer. How many whole weeks are there in three years? How many left over days that do not make up a whole week? Use GHCi to perform your calculation.
Exercise 8. Rewrite the program age.hs
without using string concatenation.
Exercise 9. Write a program to print your name, age, and special talent. Each piece of information should be on a separate line.
Exercise 10. We had a cursory discussion of syntactic sugar in this section. This page has a list of syntactic sugar in Haskell. For in-depth discussion, see this page, this, or here.