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