Organizing values with types in Haskell
A powerful way to manage complexity in a program is to add context to values. For example, 1234 could be a plain number that you can do arithmatic on or an apartment number that of course, you cannot do arithmatic on. If we label the value somehow, then we can set some rules on what can or cannot be done with the value. In programming languages, this is done through classifying values into different types. Thus, types are collections of related values. Haskell has somewhat unique way to handle types and it makes writing type safe programs convenient.
Basic types
Some languages call these atomic types. These are the building blocks of all other types.
Bool
Collection of logical values. There are only two - True
and False
Char
Collection of all unicode values. For example, ‘a’, ‘$’, ‘1’ etc.
String
Collection of all sequences of characters, including empty string. For example, “", “a”, “aloha” etc.
Int
Finite-precision integers. Range is from $-2^{63}\ to\ 2^{63} - 1$. Efficient than Integer
because operations can be performed at hardware level.
Integer
Infinite-precision integers. Limited only by the amount of memory available.
Float
Single-precision floating point numbers.
Double
Double-precision floating point numbers.
Type notation
Type notation is of the form v :: T
and we say the type with :: replaced with “has type”. For example, we write, ‘o’ :: Char and we say, “o has type char”.
List types
List is a sequence of elements of the same type. Type notation is [T]
meaning it is a “list of type T”. For example, [False, False] :: [Bool]
. Number of elements in a list is called length. Lists can be of finite or infinite lengths and can contain elements of any type. List type does not tell anything about its length.
Tuple types
Tuple is a sequence of components of possibly different types. Type notation (T1, T2,..., Tn)
is a n-tuple whose $i^{th}$ element has type $T_{i}$. Number of components in a tuple is called its ‘arity’. Like lists, tuples can contain any type but unlike lists, they can only be of finite arity. Also, from their type, one can infer how many components they have. There can be tuples of 0, 2,…, n arity but not of 1 arity because that leads to ambiguity in syntax where parenthesis are also used to control operator precedence. As an example, (True, “hello”) :: (Bool, String).
Lists and tuples provide allow us to create types of arbitrary complexity. For example
-- a pair of Bool list and String list
([False], ["hi"]) :: ([Bool], [String])
-- a list of triple of string, list and char
[("car", 4, 'a')] :: [(String, Int, Char)]
Function types
Functions map argument of one type to result of another. Type notation is f :: T1 -> T2
, meaning, f is a function that maps arguments of type T1 to results of type T2. Again, there is no restriction on what T1 and T2 should be and thus can be any of the types discussed so far including the function types too! For example,
-- a function with tuple as input and tuple as output
absPair :: (Int, Int) -> (Int, Int)
absPair (x, y) = (abs x, abs y)
-- a list of type Bool -> Bool
[not] :: [Bool -> Bool]