Types and classes
Relevant content from Chapter 3 of Programming in Haskell 2nd Edition
Basic concepts
Type is a collection of related values. Usually displayed with the different notations explained below.
NOTATION | DESCRIPTION |
---|---|
v :: T | v is a value in the type T |
e :: T | e is an expression that has not yet been evaluated and will produce a value of type T |
Every expression needs a type, calculated prior to evaluating the expression. This is done by a process called type inference.
f
is a function that maps arguments of type A
to arguments of type B
and e is an expression of type A
, then application f e
has type B
f :: A -> B, e :: A ==> f e :: B
Type error are deemed invalid expressions and happen when an expression does not have a type.
not :: Bool -> Bool, not 3 ==> not 3 :: Bool
This is invalid because the 3 is not of type Bool
.
These types are checked before the evaluation, therefore Haskell programs are type safe.
Basic types
TYPE | DESCRIPTION | EXAMPLES |
---|---|---|
Bool | Logical values | Contains True and False |
Char | Single characters | Contains all single characters in the Unicode system. Enclosed in single quotes ' ' |
String | Strings of characters | Contains all sequences of characters and the empty string "" . Enclosed in double quotes " " |
Int | Fixed-precision integers | Contains integers, with a fixed amount of memory being used for storage. GHC range -2^63 - 2^63 - 1 |
Integer | Arbitrary-precision integers | Contains integers with as much memory as necessary being used for storage |
Float | Single-precision floating-point numbers | Contains numbers with decimal point, with fixed amount of memory being used for storage. Digits permitted after decimal point depends upon size of the number (7 total digits) |
Double | Double-precision floating-point numbers | Similar to Float , except twice as much memory is used to increase precision (14 total digits) |
List types
Sequence of elements of the same type, while being enclosed in square parentheses and separated by commas [T, T, T]
.
Number of elements is called length
. List []
of length zero is called empty list.
List of length one [[]], [T]
are called singleton lists.
Type of the list only implies information about the type in the list. Not about its length because there is no requirement for it to be finite.
[False, True, False] :: [Bool]
Tuple types
Tuple is a finite sequence of components of possibly different types, while being enclosed in round parentheses and separated by commas (T1, T2)
.
Number of components in tuple is called arity
Tuple ()
of arity
zero is called empty tuple.
Tuples (T1, T2)
of arity
two is called pair. Tuples (T1, T2, T3)
of arity
three is called triples.
Tuples (T1)
of arity
one, however are not permitted, because they would conflict with the use of parentheses t make evaluation order explicit (1 + 2) * 3
.
Type of tuple (Bool, Char)
conveys its arity
. Furthermore, there are no restrictions on types of components.
('a', (False, 'b')) :: (Char, (Bool, Char))
(['a', 'b'], [False, True]) :: ([Char], [Bool])
[('a', False), ('b', True)] :: [(Char, Bool)]
Tuples must have a finite arity
, in order to ensure tuple types can be inferred prior to evaluation.
Function types
Function is a mapping T1 -> T2
from arguments
of one type to results
of another type.
Simple way to handle the case of multiple arguments
and results, is by packaging multiple values using a list
or tuple
, this is possible because there are no restrictions on the types of arguments
and results
.
There is no restriction that functions must be total
on their argument type, meaning there may be some arguments for which the result is not defined.
> head []
*** Exception: Prelude.head: empty list
Curried functions
Functions with multiple arguments can also be handled in another way. More specifically by exploiting the fact that functions are free to return functions as results.
Consider the following definition:
add :: (Int, Int) -> Int
add' :: Int -> (Int -> Int)
add' x y = x + y
The type state it is a function that takes a argument
of type Int
and returns a result that is a function of type Int -> Int
. More precisely it states that it takes a Int
followed by another Int
and returns the result of both Int
combined.
The function add
produces the same final result as add'
, but add
takes the two arguments packaged as a pair
at once, add'
takes them one at a time.
Functions with more than just two arguments can be handled using the same way.
-- 3 Arguments, 1 Result
mult :: Int -> (Int -> (Int -> Int))
mult x y z = x * y * z
These types of functions are called curried functions
, the main advantage are that they are more flexible than their Tuple / List counterpart. That is mainly because a curried function
with only a partially filling the needed arguments and using that in combination with another function.
Furthermore, excess parentheses can be dropped, because the function arrow ->
assumes association to the right.
-- Explicit
:: Int -> (Int -> (Int -> Int))
-- Implicit
:: Int -> Int -> Int -> Int
Consequently, function application, using spacing is assumed to associate to the left and unless tupling is explicitly required, all functions with multiple arguments are normally defined as curried functions
.
-- Explicit
((mult x) y) z
-- Implicit
mult x y z
Polymorphic types
To use a function on any element, irrespective of type, polymorphic types
can be used.
> length [1, 3, 5, 7]
4
> length ["Yes", "No"]
2
> length [sin, cos, tan]
3
This is done by the inclusion of a type variable
. These must begin with a lower-case letter, usually simply named a
, b
, c
, … for example type of length
is as follows.
length :: [a] -> Int
For any type a
, the function length
has type [a] -> Int
. A type that contains one or more type variables is called polymorphic
. Hence, [a] -> Int
is a polymorphic
type / length is a polymorphic
function.
Overloaded types
To restrict polymorphic types
to certain needed features and functionality class constraint
can be used. These are written in the form C a
, where C
is the name of a class and a
is a type variable.
(+) :: Num a => a -> a -> a
In other words for any type a
that is an instance
of class Num
the function (+)
has type a -> a -> a
. A type / expression that contains one or more class constraints
is called `overloaded.
Basic classes
Class is a collection of types that support certain overloaded operations called methods
. All classes mentioned below (besides Num
, Integral
, Fractional
) can be instanced by all basic types Bool
, Char
, String
, Int
, Integer
, Float
and Double
as well as list
and tuple
.
Eq - equality types
Types whose values can be compared for equality and inequality. Function types are not instances of the Eq
class.
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
Ord - ordered types
Types that are instances of the Eq
class and in addition whose values are totally (linearly) ordered.
(<) :: a -> a -> Bool
(<=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(>=) :: a -> a -> Bool
min :: a -> a -> a
max :: a -> a -> a
Note String
, list
and tuple
are ordered lexicographically
. In order if their first components are in order, then the second components are not considered, else their second components must be in order.
Show - showable types
Types whose values can be converted into strings of characters.
show :: a -> String
Read - readable types
Types whose values can be converted from strings of characters.
read :: a -> String
The type can sometimes not be inferred by GHCi, therefore adding the type hints can be useful.
> read "('a'. False)" :: (Char, Bool)
Num - numeric types
Types whose values are numeric.
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
Only the basic types Int
, Integer
, Float
, Double
are instances of Num
.
Furthermore, negative numbers must be parenthesized, when used as arguments, to ensure correct interpretation of the minus sign.
> signum (-3)
Integral - integral types
Types that are instances of Num
and support integer division and integer remainder. (Int
, Integer
)
div :: a -> a -> a
mod :: a -> a -> a
Methods mentioned above are often called like this.
> 7 `div` 2
3
> 7 `mod` 2
1
Fractional - fractional types
Types that are instances of Num
and support fractional division and fractional reciprocation. (Float
, Double
)
(/) :: a -> a -> a
recip :: a -> a