A type is a name for a collection of related values. For example, in Haskell the basic type Bool
contains the two logical values False
and True
1 + False
The +
operator requires two numbers; but in the above expression we are trying to add 1
to False
If evaluating an expression e
would produce a value of type t
, then e
has type t, written as
e :: t
Every well-formed expression has a type, which can be automatically calculated at compile time using an algorithm called type inference
A simple typing rule for function application:
f :: A -> B
e :: A
-----------
f e :: B
All type errors are found at compile time, which makes programs safer and faster by removing the need for type checks at run time.
In GHCi, the :type
command calculates the type of an expression, without evaluating it:
not False
:type not
:type False
:type not False
Bool
denoting logical valuesChar
denoting single charactersString
denoting strings of charactersInt
denoting fixed precision integer numbersInteger
denoting arbitray precision integer numbersFloat
denoting floating-point numbersDouble
denoting double-precision floating-point numbersA list is a sequence of values of the same type
[False, True, False] :: [Bool]
['a','b','c','d'] :: [Char]
["One","Two","Three"]
In general, [t]
is the type of lists with elements of type t
NOTE
[False, True] :: [Bool]
[False, True, False] :: [Bool]
[['a'],['b','c']] :: [[Char]]
A tuple is a sequence of values of possibly different types
(False,True) :: (Bool,Bool)
(False,'a',True) :: (Bool,Char,Bool)
In general
(t1,t2,…,tn) is the type of n-tuples whose ith components have type ti for any i in 1…n.
n > 1
The reason n=1 is not allowed is because it can confuse with parenthesised expressions. n is called the arity.
(False,True) :: (Bool,Bool)
(False,True,False) :: (Bool,Bool,Bool)
('a',(False,'b')) :: (Char,(Bool,Char))
(True,['a','b']) :: (Bool,[Char])
A function is a mapping from values of one type to values of another type:
not :: Bool -> Bool
even :: Int -> Bool
In general, t1 -> t2
is the type of functions that map values of type t1
to values of type t2
not :: Bool -> Bool
not True = False
not False = True
even :: Int -> Bool
even x = (x `mod` 2) == 0
-- Inductive definition of odd/even
even1 :: Int -> Bool
even1 0 = True
even1 1 = False
even1 n = odd1 (n-1)
odd1 :: Int -> Bool
odd1 0 = False
odd1 1 = True
odd1 n = even1 (n-1)
odd1 22
odd1 21
even1 22
even1 21
The argument and result types are unrestricted; Also, there can be any number of arguments.
add :: (Int,Int) -> Int
add (x,y) = x + y
add (2, 3)
zeroto :: Int -> [Int]
zeroto n = [0..n]
zeroto 5
Functions with multiple parameters can be converted to functions with one parameter using the technique called "Currying". This is possible because the return value of a function can be a function itself!
add' :: Int -> (Int -> Int)
add' x y = x + y
-- note that the two paramaters x and y are not written in tuple-form!
add' 3 4
add'
takes an integer x as input and returns a function add' x
. In turn, this function takes an integer y as input and returns the result x + y
Note
add
and add'
produce the same final result, but add
takes its two arguments at the same time, whereas add'
takes them one at a time
add :: (Int,Int) -> Int
add' :: Int -> Int -> Int
-- Another example of Curried Function:
mult :: Int -> (Int -> (Int -> Int))
mult x y z = x * y * z
mult 2 3 4
Here, mult
takes input x
and returns a function mult x
, which in turn takes input y
and returns a function mult x y
, which finally takes as input z
and returns the result x * y * z
Curried functions are more flexible that functions on tuples, because useful functions can often be made by partially applying a Curried function. e.g.
add' 1 :: Int -> Int
take 5 :: [Int] -> [Int]
drop 5 :: [Int] -> [Int]
incr :: Int -> Int
incr = add' 1
take5 :: [Int] -> [Int]
take5 = take 5
drop5 :: [Int] -> [Int]
drop5 = drop 5
incr 5
first5 [0..7]
drop5 [0..7]
To avoid excessive parentheses when using Curried functions, we adopt the following Currying Conventions:
->
associates to the right; e.g.Int -> Int -> Int -> Int
means
Int -> (Int -> (Int -> Int))
mult x y z
means
((mult x) y) z
Unless tupling is explicitly required, all functions in Haskell are normally defined in Curried form.
A function is called polymorphic ("of many forms") if its type contains one or more type variables. For example,
len :: [a] -> Int
len [] = 0
len (_:xs) = 1 + len xs
len [1,2,3] -- a is Int
len [False,True,True,False] -- a is Bool
len ["john","alice","bob","evan","zelle"] -- a id String
In the above function, len
takes as input a list of values of type a
and returns an Int
. Type variables must begin with lower-case and typically we use a, b, c, etc.
Many of the functions defined in the standard prelude are polymorphic. For example,
fst :: (a,b) -> a
head :: [a] -> a
take :: Int -> [a] -> [a]
zip :: [a] -> [b] -> [(a,b)]
id :: a -> a
fst (10,20)
fst ('a','b')
head [1,2,3,4]
head ["john","alice","bob","evan","zelle"]
take 2 ["john","alice","bob","evan","zelle"]
take 2 [1,2,3,4]
zip [1,2,3,4] ['a','b','c','d']
zip [True,False] ["John","Alice"]
id 4
id "aa"
A polymorphic function is called overloaded if its type contains one or more class constraints. e.g.
(+) :: Num a => a -> a -> a
For any numeric type a
, (+)
takes two values of type a
and returns a value of type a
, i.e. the type of inputs and outputs of the +
operator/function must come from a numeric type such a Int
, Float
etc.
Constrained type variables can be instantiated to any types that satisfy the constraints:
1 + 2 -- a is Int
1.0 + 2.0 -- a is Float
'a' + 'b' -- a is Char; not allowed!
(*) :: Num a => a -> a -> a
negate :: Num a => a -> a
abs :: Num a => a -> a
2 * 2
2 * 2.5
negate 2.5
abs (-2.5)
-- Numbers themselves are overloaded
:type 3
Haskell has a number of type classes, including
Eq
- Equality types
Ord
- Ordered types
Show
- showable types
Read
- readable types
Num
- Numeric types
Integral
- integral types
Fractional
- fractional types
This class contains types whose values can be compared for equality and inequality using the following two methods:
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
``Bool``, ``Char``, ``String``, ``Int``, ``Integer``, ``Float``, and ``Double`` are instances of ``Eq`` class, as are ``list`` and ``tuple`` types as long as their component types are instances of ``Eq``
But function types are not in Eq
because it is not feasible to compare two functions for equality.
-- For example
False == False
'a' == 'b'
"abc" == "abc"
[1,2] /= [1,2,3]
('a',20) == ('a',30)
This class contains types that are instances of Eq
re totally (linearly) ordered, and as such can be compared by the following 6 methods:
(<) :: a -> a -> Bool
(<=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(>=) :: a -> a -> Bool
min :: a -> a -> a
max :: a -> a -> a
`
Bool
, Char
, String
, Int
, Integer
, Float
, and Double
are instances of Ord
class, as are list
and tuple
types as long as their component types are instances of Ord
False < True
min 'a' 'b'
"elegant" < "elephant"
[1,2,3] < [1,2]
('a',2) < ('b',1)
('a',2) < ('a',1)
This class contains types whose values can be converted into string of characters using the following function:
show :: a -> String
Bool
, Char
, String
, Int
, Integer
, Float
, and Double
are instances of Show
class, as are list
and tuple
types as long as their component types are instances of Show
show False
show 'a'
show 123
show [1,2,3]
show ('a',False)
This class is dual to Show
, and contains types whose values can be converted from strings of characters using the following method:
read :: String -> a
Bool
, Char
, String
, Int
, Integer
, Float
, and Double
are instances of Read
class, as are list
and tuple
types as long as their component types are instances of Read
read "False" :: Bool
read "123" :: Int
read "'a'" :: Char
read "[1,2,3]" :: [Int]
read "('a',False)" :: (Char,Bool)
not (read "False")
This class contains types who values are numeric, and as such can be processed by the following six methods:
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
(negate) :: a -> a
(abs) :: a -> a
(signum) :: a -> a
The basic types Int
, Integer
, Float
, and Double
are instances of Num
1 + 2
1.0 + 2.0
negate 3.0
abs (-3)
signum (-3)
This class contains types that are instances of the numeric class Num
, but in addition whose values are integers, asnd as such support the methods of integer divison and integer remainder:
div :: a -> a -> a
mod :: a -> a -> a
7 `div` 2
7 `mod` 2
div 7 2
mod 7 2
This class contains types that are instances of the numeric class Num
, but in addition whose values are non-integral, and as such support the methids of fractional division and fractional reciprocation.
(/) :: a -> a -> a
recip :: a -> a
7.0 / 2
recip 2.0