Exploring Haskell: Types & Classes
Code examples are adapted from Introduction to Functional Programming course.
Access to GHCi is available on repl.it to test out these snippets online.
Types
Evaluating an expression e
.
e :: t  This reads as e has type t, also used for type casting
Every valid expression has a type, which is calculated using type inference.
To get a type in GHCi use :t
which is an abbreviation for :type
command.
not False  True
:t not False  not False :: Bool
Basic Types
Some basic types that are common in other programming languages:
Bool
 logical valuesChar
 single characterString
 strings of charactersInt
 fixedprecision integersInteger
 arbitraryprecision integersFloat
 floatingpoint numbers
:t True  Bool  logical values
:t 'H'  Char  single character
:t "Hi"  [Char]  strings of characters
:t 1  Num p => p
2^64 :: Int  is out of the Int range (Overflow)
:t 2^65  Num p => p
2^65 :: Integer  36893488147419103232
:t 1.5  Fractional p => p
List Types
Lists in Haskell are polymorphic and can only contain a sequence of values with the same type.
:t [False, True, False]  [False, True, False] :: [Bool]
:t ['a', 'b', 'c', 'd']  ['a', 'b', 'c', 'd'] :: [Char]
:t [['a'], ['b', 'c']]  [['a'], ['b', 'c']]  :: [[Char]]
Tuple Types
Tuples can contain sequence of values with different types
:t (False, True)  (False, True) :: (Bool, Bool)
:t (False,'a',True)  (False, 'a', True) :: (Bool, Char, Bool)
:t ('a', (False, 'b'))  ('a', (False, 'b')) :: (Char, (Bool, Char))
:t (True, ['a', 'b'])  (True, ['a', 'b'])  :: (Bool, [Char])
Functions
A function is a mapping from values of one type to values of another type:
import Data.Char (isDigit)  Necessary for isDigit to work
:t isDigit  isDigit :: Char > Bool
:t not  not :: Bool > Bool
 Example functions
add (x, y) = x + y  add :: Num a => (a, a) > a
zeroto n = [0..n]  zeroto :: (Num a, Enum a) => a > [a]
Curried Functions
When a function returns as a result an another function it is called a curried function.
add' x y = x + y  add' :: Num a => a > a > a
Both add
and add'
produce the same result, where add
takes all arguments at the same time, and add'
can consume one at a time.
 Parenthesis in Haskell are right associative and are omitted for brevity.
mult x y z = x * y * z  mult :: Num a => a > (a > (a > a))
Why is Currying Useful?
Currying makes functions more flexible and allows partial application.
Creating a function that increments by one:
addOne = add' 1
addOne 2  3
Conventions for Currying
To avoid excess parentheses when using curried functions there are two conventions:

The
>
in type definition associates to the right.Int > Int > Int > Int  Int > (Int > (Int > Int))

Function application is associated to the left.
mult x y z  ((mult x) y) z
Unless explicitly required, all functions in Haskell are defined in the curried form.
Polymorphic Functions
A function can be called polymorphic when its type contains one or more type variables
 length takes a 'collection' of type 'a' and returns an 'Int'
:type length  length :: Foldable t => t a > Int
length [False, True]  2
length [1, 2, 3, 4]  4
 More Examples
:t fst  fst :: (a, b) > a
:t head  head :: [a] > a
:t take  take :: Int > [a] > [a]
:t zip  zip :: [a] > [b] > [(a, b)]
:t id  id :: a > a
Overloaded Functions
A polymorphic function is called overloaded if its type contains one or more class constraints.
 sum takes a list with numeric type 'a', and returns a value of type 'a'.
:t sum  sum :: (Foldable t, Num a) => t a > a
sum [1, 2, 3]  6
sum [1.1, 2.2, 3.3]  6.6
sum ['a', 'b', 'c']  error
Classes
Haskell has a number of type classes:
Num
 Numeric typesEq
 Equality typesOrd
 Ordered types
:t (+)  (+) :: Num a => a > a > a
:t (==)  (==) :: Eq a => a > a > Bool
:t (<)  (<) :: Ord a => a > a > Bool
And that's it for now.