A Taste of Haskell

Part 2

Polymorphic Types

Types that contain one or more type variables


Prelude> :t head
head :: [a] -> a
Prelude> head [1..10]
1
Prelude> head [True,False,True]
True
Prelude> head "haskell"
'h'
Prelude> head ["haskell","is","cool"]
"haskell"
          

Type variables must begin with lower-case letters
and are usually named a, b, c...

Polymorphic Types


tail :: [a] -> [a]

take :: Int -> [a] -> [a]

filter :: (a -> Bool) -> [a] -> [a]

map :: (a -> b) -> [a] -> [b]

replicate :: Int -> a -> [a]

(!!) :: [a] -> Int -> a

fst :: (a, b) -> a

snd :: (a, b) -> b
          

Typeclass Constraints

C a => indicates that the type variable a must implement the C typeclass


Prelude> :t (>)
(>) :: Ord a => a -> a -> Bool
Prelude> 2 > 1
True
Prelude> 'b' > 'a'
True
Prelude> "Haskell is great" > "Haskell is cool"
True
Prelude> [2,1,0] > [2,0,5]
True
Prelude> even > odd
No instance for (Ord (Integer -> Bool)) arising from a use of ‘>’
Prelude> '2' > 1
No instance for (Num Char) arising from the literal ‘1’
          

Built-in Typeclasses


Prelude> :info Ord
          

class Eq a => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  (<=) :: a -> a -> Bool
  (>) :: a -> a -> Bool
  (>=) :: a -> a -> Bool
  max :: a -> a -> a
  min :: a -> a -> a
  {-# MINIMAL compare | (<=) #-}
instance Ord Int -- Defined in ‘GHC.Classes’
instance Ord Float -- Defined in ‘GHC.Classes’
instance Ord Double -- Defined in ‘GHC.Classes’
instance Ord Char -- Defined in ‘GHC.Classes’
instance Ord Bool -- Defined in ‘GHC.Classes’
...
          

Built-in Typeclasses


Prelude> :info Eq
          

class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
  {-# MINIMAL (==) | (/=) #-}
instance Eq Int -- Defined in ‘GHC.Classes’
instance Eq Float -- Defined in ‘GHC.Classes’
instance Eq Double -- Defined in ‘GHC.Classes’
instance Eq Char -- Defined in ‘GHC.Classes’
instance Eq Bool -- Defined in ‘GHC.Classes’
...
          

Built-in Typeclasses


Prelude> :info Num
          

class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
  {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
...
          

Built-in Typeclasses


Prelude> :info Show
          

class Show a where
  showsPrec :: Int -> a -> ShowS
  show :: a -> String
  showList :: [a] -> ShowS
  {-# MINIMAL showsPrec | show #-} -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show Int -- Defined in ‘GHC.Show’
instance Show Char -- Defined in ‘GHC.Show’
...
          

Prelude> show 1
"1"
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> odd
No instance for (Show (Integer -> Bool))
            

Type Inference

Haskell infers types as generic as possible


Prelude> add x y = x + y
Prelude> :t add
add :: Num a => a -> a -> a
Prelude> add 2 5
7
Prelude> add 2.0 5.0
7.0

Prelude> :{
Prelude| addInts :: Int -> Int -> Int
Prelude| addInts x y = x + y
Prelude| :}
Prelude> addInts 2.0 5.0
No instance for (Fractional Int) arising from the literal ‘2.0’
          

Currying

A partially-applied function is also a function


Prelude> add x y = x + y
Prelude> :t add
add :: Num a => a -> a -> a
Prelude> :t add 1
add 1 :: Num a => a -> a

Prelude> addOne = add 1
Prelude> addOne 5
6
          

In Haskell, f :: a -> b -> c is actually
f :: a -> (b -> c)

Higher-order Functions

Functions can take functions as arguments,
and return functions as their result


Prelude> :t filter
filter :: (a -> Bool) -> [a] -> [a]
Prelude> :t (>)
(>) :: Ord a => a -> a -> Bool
Prelude> filter (>10) [1,12,5,8,26,10,7,15]
[12,26,15]

Prelude> twice f x = f (f x)
Prelude> :t twice
twice :: (t -> t) -> t -> t
Prelude> twice (*2) 5
20
Prelude> twice reverse [1..10]
[1,2,3,4,5,6,7,8,9,10]
          

Higher-order Functions

Currying is a form of higher-order functions


Prelude> :{
Prelude| add :: Num a => a -> (a -> a)
Prelude| add x = (+) x
Prelude| :}
Prelude> :t add
add :: Num a => a -> a -> a
          

Processing Lists

map applies a function to all elements of a list


Prelude> :t map
map :: (a -> b) -> [a] -> [b]

Prelude> map (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]

Prelude> :t even
even :: Integral a => a -> Bool
Prelude> map even [1..10]
[False,True,False,True,False,True,False,True,False,True]

Prelude> map reverse ["haskell","is","cool"]
["lleksah","si","looc"]
Prelude> reverse ["haskell","is","cool"]
["cool","is","haskell"]
          

Defining Functions


even n :: Integral a => a -> Bool
even n = n `mod` 2 == 0

splitAt :: Int -> [a] -> ([a], [a])
splitAt n xs = (take n xs, drop n xs)

recip :: Fractional a => a -> a
recip n = 1 / n
          

Prelude> even 9
False
Prelude> even 10
True
Prelude> splitAt 3 [1..10]
([1,2,3],[4,5,6,7,8,9,10])
Prelude> splitAt 7 "haskellcurry"
("haskell","curry")
Prelude> recip 10
0.1
          

Conditional Expressions

A condition and two results of the same type


abs :: Int -> Int
abs n = if n >= 0 then n else -n

signum :: Int -> Int
signum n = if n < 0 then -1 else if n == 0 then 0 else 1
          

The else branch must always exist!

Guards

Sequence of logical expressions, with their own results


abs n | n >= 0     = n
      | otherwise  = -n

signum n
  | n < 0      = -1
  | n == 0     = 0
  | otherwise  = 1
          

otherwise matches anything and is a safe way to handle all other cases - and avoid an error!

Pattern Matching

Match argument values against patterns


not :: Bool -> Bool
not True  = False
not False = True

(&&) :: Bool -> Bool -> Bool
True  && True  = True
True  && False = False
False && True  = False
False && False = False
          

A non-exhaustive pattern can cause an exception

Pattern Matching

Underscores match anything; variables bind to values


(&&) :: Bool -> Bool -> Bool
True  && True  = True
_     && _     = False

(&&) :: Bool -> Bool -> Bool
True  && b  = b
False && _  = False

(&&) :: Bool -> Bool -> Bool
b && b = b
_ && _ = False
          

Values may not be evaluated due to lazy evaluation

Pattern Matching with Lists


alpha :: [Int] -> Bool
alpha [0,_,_] = True
alpha _       = False

alpha' :: [Int] -> Bool
alpha' (0:_) = True
alpha' _     = False
          

Prelude> alpha [0,1,2]
True
Prelude> alpha [0,1]
False
Prelude> alpha' [0]
True
Prelude> alpha' [0..]
True
          

Pattern Matching with Lists


head :: [a] -> a
head (x:_) = x

tail :: [a] -> [a]
tail (_:xs) -> xs

dropThird (x1:x2:_:xs) = x1 : x2 : xs
dropThird xs           = xs
          

Prelude> dropThird [1..10]
[1,2,4,5,6,7,8,9,10]
Prelude> 1:2:3:4:5:6:[]
[1,2,3,4,5,6]
          

Pattern Matching with Tuples


fst :: (a,b) -> a
fst (x,_) = x

snd :: (a,b) -> b
snd (_,x) = x

third :: (a,b,c) -> c
third (_,_,x) = x
          

Prelude> third (1, True, "haskell")
"haskell"
Prelude> third ('a', even, (False, 10.0))
(False,10.0)
          

Where Declarations

Bound to a scope (indicated by indentation)


qsort []     = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
  where smaller = filter (<= x) xs
        larger  = filter (> x) xs

dist (x1,y1) (x2,y2) = sqrt (dx + dy)
  where
    dx = (x1 - x2) ^ 2
    dy = (y1 - y2) ^ 2
          

Do not use tabs - the compiler will complain!

Let Expressions

Can be used wherever expressions are allowed


dist (x1,y1) (x2,y2) =
  let dx = (x1 - x2) ^ 2
      dy = (y1 - y2) ^ 2
   in sqrt (dx + dy)
          

Lambda Expressions

The symbol \ stands for the Greek letter λ


\ x -> 2 * x
          

Prelude> (\x y -> x + y) 2 5
7

Prelude> filter (\x -> x `mod` 7 == 0) [1..100]
[7,14,21,28,35,42,49,56,63,70,77,84,91,98]

Prelude> records = [(1,True,"haskell"), (2,False,"curry")]
Prelude> fmap (\(x,_,y) -> (10 * x,head y)) records
[(10,'h'),(20,'c')]
          

Exercises

Part 2

Continue to part 3...