A Taste of Haskell

Part 3

Imports


-- Import all functions from Data.List (including intersperse)
import Data.List
-- Import isAlpha only, unqualified
import Data.Char (isAlpha)
-- Import isUpper only, qualified
import qualified Data.Char as C (isUpper)

spaced   = intersperse ' ' "Haskell"
original = filter isAlpha spaced
initial  = filter C.isUpper spaced
{-
spaced   becomes "H a s k e l l"
original becomes "Haskell"
initial  becomes "H"
-}
          

Notice single- and multi-line comments!

Function Application

The $ operator is a convenience to avoid parenthesis


f $ a = f a
          

Prelude> negate (2 + 5)
-7
Prelude> negate $ 2 + 5
-7

Prelude> sum (map (^2) (filter even [1..100]))
171700
Prelude> sum $ map (^2) $ filter even [1..100]
171700
          

Function Composition

The dot operator (.) composes two functions


(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)
          

Prelude> odd = not . even
Prelude> :t odd
odd :: Integral a => a -> Bool
Prelude> :t even
even :: Integral a => a -> Bool
Prelude> :t not
not :: Bool -> Bool

Prelude> twice f = f . f
Prelude> twice (*2) 5
20
          

Function Composition

Composition is associative


f . (g . h) = (f . g) . h
          

Prelude> sumSqrEven ns = sum (map (^2) (filter even ns))
Prelude> sumSqrEven [1..100]
171700
Prelude> sumSqrEven' = sum . map (^2) . filter even
Prelude> sumSqrEven' [1..100]
171700
          

sumSqrEven' is in pointfree style, the others are not


Prelude> sumSqrEven'' xs = sum $ map (^2) $ filter even xs
171700
          

Pointfree Style

Often tidier, focus on the functions rather than the data


countA xs = length $ filter (== 'a') xs

countA' = length . filter (== 'a')

fn x = ceiling (negate (tan (cos (max 50 x))))

fn' = ceiling . negate . tan . cos . max 50

sumSquareFilter f xs = sum $ map (^2) $ filter f xs

sumSquareFilter' f = sum . map (^2) . filter f
          

List Comprehension

Construct new lists from existing lists


Prelude> [x ^ 2 | x <- [1..5]]
[1,4,9,16,25]

Prelude> [(x,y) | x <- [1,2,3], y <- "ab"]
[(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]

Prelude> [(x,y) | y <- "ab", x <- [1,2,3]]
[(1,'a'),(2,'a'),(3,'a'),(1,'b'),(2,'b'),(3,'b')]

Prelude> [(x,y) | x <- [1,2,3], y <- [x..4]]
[(1,1),(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(3,3),(3,4)]

Prelude> factors n = [x | x <- [1..n], n `mod` x == 0]
Prelude> factors 15
[1,3,5,15]
          

Recursive Functions


factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)

product :: Num a => [a] -> a
product []     = 1
product (n:ns) = n * product ns

length :: [a] -> Int
length []     = 0
length (_:xs) = 1 + length xs

(++) :: [a] -> [a] -> [a]
[]     ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
          

Recursive Functions


insert :: Ord a => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys)
  | x <= y     = x : y : ys
  | otherwise  = y : insert x ys

isort :: Ord a => [a] -> [a]
isort []     = []
isort (x:xs) = insert x (isort xs)

zip :: [a] -> [b] -> [(a,b)]
zip []     _      = []
zip _      []     = []
zip (x:xs) (y:ys) = (x,y) : zip xs ys
          

Prelude> zip "haskell" [1..]
[('h',1),('a',2),('s',3),('k',4),('e',5),('l',6),('l',7)]
          

Folds

Encapsulate patterns of recursion


-- Older versions of GHC
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

-- GHC 7.10 and newer
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
          

Prelude> foldr (+) 0 [1,2,3]
6
Prelude> 1 + (2 + (3 + 0))
6
          

sum xs = foldr (+) 0 xs

sum = foldr (+) 0
          

Folds


Prelude> product = foldr (*) 1
Prelude> product [1..5]
120

Prelude> length = foldr (\_ n -> n + 1) 0
Prelude> length []
0
Prelude> length [1..100]
100

Prelude> any f = foldr (\x b -> f x || b) False
Prelude> any even []
False
Prelude> any even [1,3,5,6]
True
          

Left vs. Right Folds

In a left fold, the operator associates to the left


foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
          

Prelude> foldr (^) 0 [2,3,4]
8
Prelude> 2 ^ (3 ^ (4 ^ 0))
8

Prelude> foldl (^) 0 [2,3,4]
0
Prelude> ((0 ^ 2) ^ 3) ^ 4
0
          

Type Declarations

Introduce a new name for an existing type


type Coordinate = Integer

type Position = (Coordinate, Coordinate)

pos :: Position
pos = (10, 7)

swap :: Position -> Position
swap (x, y) = (y, x)
          

Type declarations cannot be recursive


type Tree = (Int, [Tree])
          

Data Declarations

Declare a completely new type


data Direction = North | South | East | West

move :: Direction -> Position -> Position
move North (x, y) = (x, y + 1)
move South (x, y) = (x, y - 1)
move East  (x, y) = (x + 1, y)
move West  (x, y) = (x - 1, y)
          

Prelude> move North (10,0)
(10,1)
          

Type and constructors must begin with capital letters

Data Declarations

Constructors can also have arguments


data Shape = Circle Float | Rect Float Float

shape = Rect 10.5 8.2

area :: Shape -> Float
area (Circle r) = pi * r ^ 2
area (Rect h w) = h * w
          

Constructors are a special type of functions


Prelude> :t Circle
Circle :: Float -> Shape
Prelude> :t Rect
Rect :: Float -> Float -> Shape
          

Parameterised Data Declarations


data Maybe a = Nothing | Just a

safediv :: Int -> Int -> Maybe Int
safediv _ 0 = Nothing
safediv m n = Just (m `div` n)

safehead :: [a] -> Maybe a
safehead [] = Nothing
safehead xs = Just (head xs)
          

Prelude> safediv 10 3
Just 3
Prelude> safediv 10 0
Nothing
Prelude> safehead [1..]
Just 1
Prelude> safehead []
Nothing
          

Recursive Data Declarations


data Tree a = Leaf | Node (Tree a) a (Tree a)

tree = Node (Node Leaf 3 (Node Leaf 4 Leaf))
            5
            (Node (Node Leaf 6 Leaf) 7 Leaf)

occurs :: Eq a => a -> Tree a -> Bool
occurs _ Leaf         = False
occurs x (Node l y r) = x == y || occurs x l || occurs x r
          

Prelude> occurs 6 tree
True
Prelude> occurs 2 tree
False
          

Instance Declarations


class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
  {-# MINIMAL (==) | (/=) #-}

instance Eq a => Eq (Tree a) where
  Leaf         == Leaf         = True
  (Node a b c) == (Node d e f) = a == d && b == e && c == f
  _            == _            = False
          

Prelude> tree = Node (Node Leaf 'a' Leaf) 'b' Leaf
Prelude> tree == Node Leaf 'b' Leaf
False
Prelude> tree /= Node (Node Leaf 'a' Leaf) 'b' Leaf
False
          

Derived Instances

Some instances can be derived automatically


data Tree a = Leaf | Node (Tree a) a (Tree a)
  deriving (Show, Eq, Ord)
          

Prelude> Leaf == Leaf
True
Prelude> Leaf < Node Leaf 'a' Leaf
True
Prelude> tree = Node Leaf 2 Leaf
Prelude> tree > Node Leaf 1 Leaf
True
Prelude> tree > Node (Node Leaf 0 Leaf) 1 Leaf
False
Prelude> print tree
Node Leaf 2 Leaf
          

Exercises

Part 3