Numeric Types
Interactively explore some of Haskell's basic numeric types.
We'll cover the following
The two most central concepts of Haskell are types and functions.
- Types are collections of values that behave similarly, e.g., numbers or strings.
- Functions can be used to map values of one type to another.
In this lesson, we will look at some of the most basic numeric types in Haskell, and some predefined functions that operate on these types.
GHCI
For the examples in this lesson, we will use ghci
, an interactive Haskell interpreter that comes bundled with the Glasgow Haskell Compiler (GHC). ghci
is a so called REPL, which means it will:
- Read your input (a Haskell expression)
- Evaluate the expression
- Print the result
- Loop (i.e., you can repeatedly enter expressions)
You can use the following widget to enter the examples of this lesson or just play around with ghci
.
Numbers
In GHCI, you should see a prompt like this:
GHCi, version 8.8.3: https://www.haskell.org/ghc/ :? for help
Prelude>
Let’s start with some simple arithmetic:
Prelude> 1 + 2
3
Prelude> 5 * (7 - 1)
30
Prelude> 3 / 2
1.5
The numeric expressions are evaluated as we might expect. In the first example, 1
and 2
are values of a numeric type and +
is a function that combines two numbers to obtain their sum.
There are different numeric types in Haskell. The most important ones are:
Int
for (typically 64 bit) integers (e.g.5
,0
,-32
).Integer
for integers of arbitrary size (often called “big integers” in other languages).Double
for 64 bit floating point numbers (e.g.1.5
,-0.007
).
The operators +
, *
, -
can be used with either of those, while /
is not available for integers.
Type inference and type annotations
Note that in the above expressions, we did not need to specify the types of the numbers. This is because Haskell uses type inference to automatically determine the type of expressions. This can be done by inspecting the values and functions occurring in the expression. For example, as the division operator /
cannot be used between integers, the type of 3 / 2
can not be Int
.
We can, however, explicitly annotate the types of values. For example, one could write:
Prelude> (1 :: Int) + (2 :: Int)
3
In general, type annotations are optional, but they can add clarity to the code when the types of values are not obvious.
Let’s use type annotations to demonstrate that /
does not work on integers:
Prelude> (3 :: Int) / (2 :: Int)
<interactive>:12:1: error:
• No instance for (Fractional Int) arising from a use of ‘/’
• In the expression: (3 :: Int) / (2 :: Int)
In an equation for ‘it’: it = (3 :: Int) / (2 :: Int)
In fact, trying to divide an Int
by another Int
yields a type error. The error comes with an explanation: Haskell expected the arguments of /
to be fractional numbers, but they were integers.
In general, we can only use arithmetic operators to combine values of the same type, or we will obtain type errors.
Prelude> (2 :: Int) + (3 :: Double)
<interactive>:8:15: error:
* Couldn't match expected type `Int' with actual type `Double'
* In the second argument of `(+)', namely `(3 :: Double)'
In the expression: (2 :: Int) + (3 :: Double)
In an equation for `it': it = (2 :: Int) + (3 :: Double)
Type errors are discovered by the compiler during a compilation phase called type checking, which also handles type inference. A Haskell program with type errors cannot be successfully compiled. The advantage of type checking is that potential bugs can be caught upfront during compilation and will not lead to runtime errors during the execution of the program.
Numeric Expressions
The expression 3.5 + (3 :: Int)
yields a type error. Why?
The +
operator cannot be used on integers.
Type inference infers that 3.5 has to be a floating point number. But +
cannot be used on numbers of different types.
3.5 does not have a type annotation.