0% found this document useful (0 votes)
131 views356 pages

Lec1 7

This document provides an overview of the CS 101c Functional Programming course. The course will introduce functional programming concepts using Haskell. It will cover basics of functional programming in the first half and focus on monads in the second half. The course aims to address common problems in programming like bugs and code complexity through the functional programming paradigm of avoiding state mutation. Haskell was chosen as the teaching language because it is a purely functional language, allowing students to fully learn functional programming concepts.

Uploaded by

David Corbin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
131 views356 pages

Lec1 7

This document provides an overview of the CS 101c Functional Programming course. The course will introduce functional programming concepts using Haskell. It will cover basics of functional programming in the first half and focus on monads in the second half. The course aims to address common problems in programming like bugs and code complexity through the functional programming paradigm of avoiding state mutation. Haskell was chosen as the teaching language because it is a purely functional language, allowing students to fully learn functional programming concepts.

Uploaded by

David Corbin
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 356

CS 101c

Functional Programming
Lecture 1: April 2, 2014

Overview, philosophy, basics

Functional Programming: Spring 2014

Today
Course overview and policies
Motivation (course philosophy)
Introduction to Haskell

Functional Programming: Spring 2014

Course overview

Functional Programming: Spring 2014

Course policies
9 credits, graded
No midterm or final
6 assignments, graded from 0-3 as in CS 1
Grades:

A: 16-18
B: 13-15
C: 11-12
D: 10
F : < 10

Functional Programming: Spring 2014

Assignments

Like CS 1:

grades are 0 (no good), 1, 2, 3 (near-perfect)


multiple sections
grade is minimum of section grades
one week rework
submitted through csman

Functional Programming: Spring 2014

Web site
On moodle.caltech.edu
Password is iheartmonads

Functional Programming: Spring 2014

Textbooks

None required, some recommended:

Introduction to Functional Programming Using


Haskell, 2nd ed. by Bird
Real World Haskell, by O'Sullivan, Goertzen,
Stewart
Purely Functional Data Structures by Okasaki
Learn You a Haskell For Great Good by Lipovaca

I will adapt material from these but will not


follow them particularly closely
Other readings may come up too (papers,
blog posts)

Functional Programming: Spring 2014

Textbooks

Functional Programming: Spring 2014

Textbooks

Functional Programming: Spring 2014

Textbooks

Functional Programming: Spring 2014

Textbooks

Functional Programming: Spring 2014

Course outline

First half:

Basic functional programming


Evaluation, induction, proving correctness
Core Haskell
"Thinking functionally"

Functional Programming: Spring 2014

Course outline

Second half: Monads

Theory:
notions of computation
monad laws

Applications:
computations that may fail (Maybe monad)
computations that return multiple values (list monad)
computations that may fail in multiple specific ways with
error recovery (Error monad)
computations that do input/output (IO monad)
computations that manipulate state (State monad)
imperative programming in Haskell

Functional Programming: Spring 2014

Course philosophy/reason for being

Functional Programming: Spring 2014

What is wrong with programming?

Some things that are wrong are:

Too many bugs (too difficult to write correct


programs)
Too much code (code is at too low a level)
Too hard to exploit concurrent and parallel
programming

Functional Programming: Spring 2014

What is wrong with programming?

Functional programming (FP) may offer a


solution to these problems

FP code typically has far fewer bugs than non-FP


code ("If it compiles, it's probably correct")
FP code typically at a much higher level than nonFP code (fewer lines of code to say the same thing)
FP code naturally lends itself to parallelization

Functional Programming: Spring 2014

What is Functional Programming?


Difficult to define precisely
"You know it when you see it"
Some common threads, several axes of
variation

Functional Programming: Spring 2014

What is Functional Programming?

Thread 1: Functions are data

Functions can be passed as arguments to other


functions
Functions can be returned as return values of
functions
Functions can be created on-the-fly

N.B. By this standard, many non-FP


languages (e.g. Python) would qualify as
functional languages

Functional Programming: Spring 2014

What is Functional Programming?

Thread 2: State mutation is discouraged or


forbidden completely

Emphasis on using immutable data structures


(singly-linked lists, trees) instead of mutable ones
(arrays, hash tables)
Use of recursion for looping instead of counting up
or down a state variable
Use of helper functions with extra arguments
instead of mutable local state variables

Functional Programming: Spring 2014

Problem(s) with mutation

State mutation is a very fertile source of bugs

e.g. aliasing
references to objects behave differently than copies
of objects
"off-by-one" errors in loops

State mutation makes it harder to have a


mathematical theory of programming

must model the locations where data kept


semantics are time-dependent
recall environment model in CS 4

Functional Programming: Spring 2014

Advantages of mutation

Many programming problems are most


naturally expressed in terms of mutating state
variables

State mutation maps well onto current


microprocessor designs

e.g. simulations

imperative code can thus run very efficiently

Many familiar data structures and algorithms


absolutely require the ability to mutate state

e.g. see any standard algorithms textbook

Functional Programming: Spring 2014

Programming paradigms

Different programming "paradigms" are


largely distinguished by the way they handle
mutation

Imperative: allow mutation with no restrictions


Object-oriented: allow mutation internally in objects
only (in response to a method call)
Functional: discourage mutation
Purely functional: disallow mutation entirely!

This illustrates how important the "mutation


problem" has been in the evolution of
programming languages

Functional Programming: Spring 2014

"Functional style"

Learning to write programs without mutation is


one of the hardest aspects of learning
functional programming

like learning to program from scratch all over again

Many functional languages (e.g. Scheme,


Ocaml) allow mutation, allowing programmers
to "cheat" and fall back on imperative habits if
they want to
Pure functional languages make this much
harder, forcing you to learn functional style

Functional Programming: Spring 2014

Other FP features

Some (but not all) functional languages have


features such as:

strong static type systems


powerful type definition facilities
type inference
interactive interpreters
support for monads
support for concurrency
support for parallel programming

Functional Programming: Spring 2014

Survey of FP languages
Lisp: Original FP language (1958!).
Dynamically-typed, AI orientation, macros,
fast compilers, "industrial strength"
Scheme: Modern, "cleaned-up" Lisp, stronger
FP orientation, hygienic macros
Clojure: Lisp for the JVM, very functional,
strong concurrency orientation
Erlang: Dynamically-typed, concurrent FP
language; emphasis is on massive
concurrency using message-passing

Functional Programming: Spring 2014

Survey of FP languages
Scala: Hybrid OO/FP language, runs on JVM,
statically-typed, complex but powerful type
system
Standard ML: Statically-typed functional
language with imperative programming
support (mutable references and arrays)
Ocaml: Similar to Standard ML, OO
extensions, fast compilers and fast code
Haskell...

Functional Programming: Spring 2014

Haskell
A non-strict, purely functional language
Non-strict ("lazy"): expressions are never
computed unless their values are needed
Purely functional:

no mutable values (except with monads)


simple computational model (substitution model)
easy to reason about code correctness

Functional Programming: Spring 2014

Haskell

Other features of Haskell:

Statically typed, compiled language


Extremely advanced type system
Generic programming using type classes
Imperative programming (and more!) using monads
Simulate OO features using existential types
Can even simulate dynamically-typed languages
(with the Typeable type class)!

Functional Programming: Spring 2014

Why Haskell?
Functional programming is a new way to think
about programming (a new programming
paradigm)
To learn a new programming paradigm, it is
useful to study the purest instance of it
Almost all other FP languages let you "cheat"
and program non-functionally
Haskell doesn't, so you must learn to program
functionally

though monads allow "controlled cheating"

Functional Programming: Spring 2014

Why Haskell?
Much of the cutting-edge work in functional
programming is being done in Haskell
New FP abstractions are coming up all the
time, usually first in Haskell

arrows
applicative functors
monoids
iteratees
functional dependencies / type families
etc.

Functional Programming: Spring 2014

Why Haskell?

Haskell is also a practical programming


language

very advanced compiler (ghc)


interactive interpreter (ghci)
fast executables
large libraries
very helpful and rapidly-growing user community

Functional Programming: Spring 2014

Personal observations

Functional programming tends to spoil you as


a programmer (hard to go back to non-FP
languages)

Quote: "Haskell is bad, it makes you hate other


languages."

When you get used to working at a high level,


with strong type systems to check your work,
it's hard to give that up
Functional languages are more fun!

Functional Programming: Spring 2014

Beginning of details

Functional Programming: Spring 2014

About Haskell
Haskell is a compiled language
Can also be run using an interpreter

Compiler we'll use: ghc (Glasgow Haskell


Compiler)

state-of-the-art, many language extensions

Interpreter we'll use: ghci (ghc interactive)

with some restrictions

part of the ghc program

Debugger: integrated into ghci

Functional Programming: Spring 2014

Haskell as a calculator
We'll work mostly with ghci at first
Start up ghci

% ghci
[... some descriptive text ...]
Prelude>

Enter expressions at the prompt, hit


<return> to evaluate them

Prelude> 2 + 2<return>
4

Woo hoo!

Functional Programming: Spring 2014

Haskell as a calculator
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> sum [1..10]
55
Prelude> foldr (*) 1 [1..10]
3628800

[1..10] is a list from 1 to 10


Function calls (like sum) don't require
parentheses around arguments

Functional Programming: Spring 2014

Haskell code in files


Haskell source code files have names that
end in .hs and (by convention) start with a
capital letter (e.g. Foo.hs)
Files normally define a module of Haskell
code
Start file Foo.hs like this:

module Foo where


...code goes here...

(More sophisticated module declarations


exist)

Functional Programming: Spring 2014

Comments

Single-line comments start with -- and go to


the end of the line

-- this is a comment

Multi-line comments start with {- and go to


the matching -}

{- this is a
multiline
comment -}

Multiline comments can nest!

Functional Programming: Spring 2014

File/ghci interaction
ghci is good for interactive experimentation/
testing of code
Cannot enter arbitrary code into ghci (some
limitations)

though newer versions of ghci are getting closer to


supporting full Haskell language

Best approach:

write code in source code files


load into ghci, test

Functional Programming: Spring 2014

File/ghci interaction

Example: file Foo.hs:

module Foo where


double :: Int -> Int
double x = 2 * x

Load into ghci and test:

Prelude> :load Foo.hs


*Foo> double 10
20

Functional Programming: Spring 2014

File/ghci interaction

:load is an example of a ghci-specific


command (not part of Haskell language)

instruction to the interpreter: load a particular file

Can abbreviate this as :l

Prelude> :l Foo.hs

When loading a module, the prompt changes


to reflect the new module

*Foo>

The * means that all definitions in the module


Foo are in scope

Functional Programming: Spring 2014

Function definitions

Definition of the double function in Foo.hs:

double :: Int -> Int


double x = 2 * x

The first line is the function's type declaration


The :: means "has the type:"

so double "has the type" Int -> Int

Int is the name of the type of (machine-level)


integers
-> means that this is a function which takes
one Int argument and produces one Int
result

Functional Programming: Spring 2014

Function definitions

Type declarations can be omitted:

double x = 2 * x
The compiler will try to infer what the proper type
should be (type inference)
This will usually work, but it's almost always a better
idea to write down the type declaration explicitly

good documentation
clear statement of programmer intent

Inferred types are often more general than


you might want, e.g.

double :: Num a => a -> a


Functional Programming: Spring 2014

Function definitions

The definition of the function double:

double x = 2 * x

is an equation describing how to transform the


input (x) into the output
Haskell functions are written as a series of
equations describing how all possible inputs
are transformed into the outputs

Functional Programming: Spring 2014

Types

Consider:

double :: Int -> Int


double x = 2 * x

Haskell is strongly statically typed


All values have a type which is known at
compile time
Types are checked during compilation

errors mean code doesn't compile

Functional Programming: Spring 2014

Types

Consider:

double :: Int -> Int


double x = 2 * x

x has the type Int


The return value of the function has type Int
double has the functional type Int -> Int
double is a value, just like x is
Functions are values in functional languages!

Functional Programming: Spring 2014

Types

You can use ghci to query the type of any


value

Prelude> :load Foo.hs


*Foo> :type double
double :: Int -> Int
*Foo> :t double
double :: Int -> Int

:t is short for :type

Functional Programming: Spring 2014

Pattern matching

Most functions have more than one equation:

factorial :: Integer -> Integer


factorial 0 = 1
factorial n = n * factorial (n 1)

Integer is the type of arbitrary-precision


integers
Given an input, Haskell selects the appropriate
equation to use by pattern matching
Left-hand sides of equations are patterns to
match

Functional Programming: Spring 2014

Pattern matching
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n 1)

Given a function call e.g. factorial 3:

Haskell tries to match with factorial 0


0 doesn't match 3 (failure)
Then tries to match with factorial n
This will match if n is 3
evaluates 3 * factorial (3 1), etc.

Functional Programming: Spring 2014

Pattern matching
Much more to say about pattern matching in
subsequent lectures
Pattern matching is a pervasive feature of
Haskell programming
Beginning programmers often under-utilize it
in favor of more familiar approaches
For instance

Functional Programming: Spring 2014

if expression

More conventional way to write factorial


function:

factorial :: Integer -> Integer


factorial n = if n == 0
then 1
else n * factorial (n 1)

Note: Haskell has indentation-sensitive


syntax, sort of like Python but less rigid
then and else must not be to the left of if

Functional Programming: Spring 2014

if expression
factorial :: Integer -> Integer
factorial n = if n == 0
then 1
else n * factorial (n 1)

if has the form:

if <test> then <expr1> else <expr2>

<test> must have type Bool (boolean)

whose values are True and False

<expr1> and <expr2> must both have same


type
cannot leave out <expr2>

Functional Programming: Spring 2014

Next time
More Haskell basics
Evaluation in Haskell

Functional Programming: Spring 2014

CS 101c
Functional Programming
Lecture 2: April 7, 2014

Evaluation

Functional Programming: Spring 2014

Today
More Haskell basics
Introduction to Haskell's evaluation model

Functional Programming: Spring 2014

More Haskell basics

Functional Programming: Spring 2014

Scalar data types

Haskell has a fairly standard assortment of


scalar data types:

Int
Integer
Float
Double
Char
Bool

Functional Programming: Spring 2014

Int and Integer


The two basic integral types are Int and
Integer
Int stands for machine-level integers (32 or
64 bits, as the case may be)
Integer stands for arbitrary-precision
integers
If there is no compelling reason to use Int,
use Integer
(Other integral types also exist)

Functional Programming: Spring 2014

Float and Double


The two basic approximate real number types
are Float and Double
Both map onto corresponding machine types
(IEEE Floats, IEEE Doubles)

Functional Programming: Spring 2014

Char
The Char type represents a single Unicode
character (16-bit)
Character literals written between single quotes
'l' 'i' 'k' 'e' ' ' 't' 'h' 'i' 's'
Standard character escape sequences like \n
(newline), \t (tab) and \\ (backslash) are
supported

Functional Programming: Spring 2014

Bool
The Bool type represents boolean (true/false)
values
There are two values in the Bool type: True and
False
These are actually data constructors, and the
Bool type is not hard-wired into the language

instead, just part of the Prelude (core libraries)

Functional Programming: Spring 2014

Compound data types

Haskell has a number of built-in compound data


types

Examples:

meaning made of multiple instances of simpler data


types
lists
strings
tuples

Also many other compound types in libraries

arrays, sets, maps, etc.

Functional Programming: Spring 2014

Lists
Lists in Haskell are comprised of multiple values
of a single type (list of Int, list of Float, etc.)
Literal lists are written with values between
square brackets, separated by commas
[1, 2, 3, 4, 5]
Type of lists is written as the type of the
elements, surrounded by square brackets
[1, 2, 3, 4, 5] :: [Integer]

Functional Programming: Spring 2014

Lists
Empty list written as []
Lists are constructed using the : (cons) operator

1 : (2 : (3 : (4 : (5 : []))))
== [1, 2, 3, 4, 5]

We will examine lists in much greater detail next


lecture

Functional Programming: Spring 2014

Strings
Strings are represented as lists of Chars
Advantage: can use all the list functions on
strings
Disadvantage: this is a very expensive way to
represent strings!

alternatives are available e.g. ByteString and Text

There is a data type called String which is an


alias for [Char] (list of Char)
Literal strings written between double quotes
"like this"; usual escapes apply

Functional Programming: Spring 2014

Tuples
A tuple is a sequence of values inside
parentheses, separated by commas
Tuples can contain values of different types:

(1, "foo", 3.14) :: (Integer, String, Double)

There are no empty tuples or length-1 tuples


Can construct tuples using "tuple constructors"

(,) 1 2 (1, 2)
(,,) 1 "foo" 3.14 (1, "foo", 3.14)

but usually just write out literal tuples

Functional Programming: Spring 2014

Identifier syntax

Haskell has fairly conventional syntax for


identifiers

letters from a-z, A-Z, numbers from 0-9, also _


also ' character allowed e.g. foo', foo''
first character cannot be digit or '
first character must be capitalized in some
circumstances:

type names (Int, Integer, Float, Char)


module names (Prelude, Data.List)
data constructor names (later lecture)
type constructor names (later lecture)
nowhere else!

Functional Programming: Spring 2014

Operator syntax

There is no fixed set of operators

operators are not "hard-wired" into the language

Operator identifiers are made up of "operator


characters" (usual symbolic characters on
keyboard)
Operators are just syntactic sugar for twoargument functions in infix position
Can convert an operator to a two-argument
function by surrounding it with parentheses
(+) 2 2 4

Functional Programming: Spring 2014

Function Operator
A two-argument function can be written as an
operator by surrounding it with backticks (the `
character)
Example:
Prelude> mod 5 2
1
Prelude> 5 `mod` 2
1
This often makes code more readable

Functional Programming: Spring 2014

Defining new operators


Operators can be defined as easily as functions:
(%%) :: Integer -> Integer -> Integer
(%%) x y = x + 2 * y
or write second line as:
x %% y = x + 2 * y
Test:
Prelude> 10 %% 2
14
Cool!

Functional Programming: Spring 2014

Operator precedence
Haskell operators have one of ten precedence
levels (0-9); 0 is lowest, 9 is highest
Function application has higher precedence than
anything else (conceptually, level 10)

double 10 + double 20 (double 10) +


(double 20)

Can use ghci's :info (:i) command to tell you


what the precedence for an operator is

Functional Programming: Spring 2014

Operator precedence
Prelude> :info *
class (Eq a, Show a) => Num a where
...
(*) :: a -> a -> a
...
-- Defined in GHC.Num
infixl 7 *
We'll explain the class stuff later
infixl 7 * means the * operator is left-associative,
precedence level 7

Functional Programming: Spring 2014

Operator precedence
Prelude> :info +
infixl 6 +

+ has lower precedence than *

Prelude> :i ^
infixr 8 ^
^ (exponentiation) has higher precedence than *, right
associative (so a^b^c a^(b^c))
N.B. ^ operator used when raising to integer power only;
use ** for raising to float power (also infixr 8)

Functional Programming: Spring 2014

Operator precedence
When defining a new operator, default
precedence is 9 (highest); default associativity is
left
Can specify precedence/associativity explicitly

(%%) :: Integer -> Integer -> Integer


x %% y = x + 2 * y
infixl 7 %%

infixl left associative


infixr right associative
infix non-associative

Functional Programming: Spring 2014

Functions with multiple arguments


Conceptually, Haskell functions all take only a
single argument
We need to be able to write functions that take
multiple arguments
Two basic ways to do this

Functional Programming: Spring 2014

Functions with multiple arguments

Way 1:

add :: (Integer, Integer) -> Integer


add (x, y) = x + y

The add function takes as its only argument a


two-tuple of Integers, returning an Integer
The left-hand side of the equation pattern
matches the two-tuple, binding x and y locally in
the equation (scope includes the right-hand side
of the equation only)
Call this function like this: add (3, 2) 5

Functional Programming: Spring 2014

Functions with multiple arguments

Way 2:

add2 :: Integer -> Integer -> Integer


add2 x y = x + y

The add2 function takes as its only argument a


single Integer, returning a value with the
functional type Integer -> Integer
N.B. The function arrow -> associates to the
right, so the type signature is really
Integer -> (Integer -> Integer)

Functional Programming: Spring 2014

Functions with multiple arguments

Way 2:

add2 :: Integer -> Integer -> Integer


add2 x y = x + y

Calling this function:


add2 3 4 7
Function calls associate to the left, so this is
really ((add2 3) 4)
What does (add2 3) mean?

Functional Programming: Spring 2014

Functions with multiple arguments

Can use partially-applied functions as functions:

add_3 :: Integer -> Integer


add_3 = add2 3
Prelude> add_3 10
13

This behavior is called "currying"

after Haskell Curry, a logician


(also inspired a programming language)

Functional Programming: Spring 2014

Functions with multiple arguments

Pitfall:

square :: Integer -> Integer


square x = x * x
Prelude> square square 4

Get nasty error message


Haskell interprets this as (square square) 4
which doesn't make sense
Recall: function application associates to the left!
Need to write square (square 4)

Functional Programming: Spring 2014

Operator sections

Can do the equivalent of currying on operators


too:

Prelude> (*2) 10
20
Prelude> (9/) 3
3

These are called "operator sections"

squared :: Integer -> Integer


squared = (^2)

Functional Programming: Spring 2014

Local definitions

Often useful to have local definitions in functions:

local values: compute once, use multiple times


local functions: use only in the scope of the outer
function

Two ways to do this in Haskell:

let expressions
where declarations

Functional Programming: Spring 2014

let expression
let expression defines a local value or values
and a scope to use it in
let x = 10 in 2 * x
20
Can define multiple local values in a single let
let x = 10
y = 100
in x - y
-90

Functional Programming: Spring 2014

let expression
Names in a let expression can depend on each
other:
let x = 10
y = x * 2
in x + y
30
let y = x * 2
x = 10
in x + y
30

Functional Programming: Spring 2014

let expression

Local definitions in a let expression can be


functions (even recursive functions):

let f x = 2 * x in f 1000
2000
let
odd n = if n == 0 then False else even (n-1)
even n = if n == 0 then True else odd (n-1)
in even 1002
True

Functional Programming: Spring 2014

let expression

Can even add type signatures to local functions:

let
odd :: Integer -> Integer
odd n = if n == 0 then False else even (n-1)
even :: Integer -> Integer
even n = if n == 0 then True else odd (n-1)
in even 1002
True

This is recommended!

Functional Programming: Spring 2014

where declaration

After a function equation in a function definition,


can add a where declaration for definitions local
to that equation

-- tail-recursive factorial
factorial_tr :: Int -> Int
factorial_tr n = iter n 1
where
iter :: Int -> Int -> Int
iter 0 r = r
iter n r = iter (n - 1) (n * r)
Functional Programming: Spring 2014

where declaration

where declaration is not an expression

Scope of where is only the equation to which it


applies

won't apply to multiple equations in the same function

Can add type signature to names bound in a


where declaration

can't write (x * 2 where x = 100^2)

not required (types inferred if not supplied) but almost


always a good idea

where generally preferred over let for local


function definitions
Functional Programming: Spring 2014

Haskell's evaluation model

Functional Programming: Spring 2014

Evaluation in Haskell
The "evaluation model" of a language are the
rules by which expressions get evaluated
Good news: Haskell's evaluation model is
generally very simple

no more than high school algebra


"equational reasoning"

Bad news: lazy evaluation complicates things


significantly in some cases
Let's walk through some examples

Functional Programming: Spring 2014

Example 1
double :: Integer -> Integer
double x = x + x

Evaluate: double (3 * 4)
Multiple possibilities exist!
In general:

pick a reducible expression (redex) and reduce it


continue until there is nothing more to reduce
the resulting value is called the normal form
which is the answer

Functional Programming: Spring 2014

Example 1
Evaluate: double (3 * 4)
Attempt 1:

reduce (3 * 4) first 12
evaluate double 12
replace double by its definition, substitute values for
arguments
evaluate 12 + 12 24

This strategy is called strict or applicative-order


evaluation
You first reduce arguments to functions to normal
forms, then substitute into function body

Functional Programming: Spring 2014

Example 1
Evaluate: double (3 * 4)
Attempt 2:

replace double by its definition, substitute unevaluated


expressions for arguments
evaluate (3 * 4) + (3 * 4)
reduce left subexpression 12 + (3 * 4)
reduce right subexpression 12 + 12
reduce remaining expression 24

This is called non-strict or normal-order evaluation


Apply functions to unevaluated expressions,
reduce only as needed to get final result

Functional Programming: Spring 2014

Example 1
Evaluate: double (3 * 4)
Attempt 3:

replace double by its definition, substitute unevaluated


expressions for arguments
evaluate (3 * 4) + (3 * 4)
both (3 * 4) subexpressions are actually the same
expression, so reduce them both at the same time
12 + 12 24

This is usually called lazy evaluation

Optimized form of normal-order evaluation

Functional Programming: Spring 2014

Strict vs. lazy

Strict evaluation:

is simple and easy to understand


may do unnecessary computations
may not terminate on well-defined problems

Lazy evaluation:

only does as much work as is needed


can give results where strict evaluation does not
can complicate reasoning about efficiency
Will this expression be evaluated? If so, when?

Haskell uses lazy evaluation


Functional Programming: Spring 2014

Example 2
infinity :: Integer
infinity = infinity + 1

Try to evaluate:

infinity
infinity + 1
(infinity + 1) + 1
((infinity + 1) + 1) + 1

Evaluation never terminates!


The expression infinity has no normal form!
Non-terminating expressions called bottom (_|_)

Functional Programming: Spring 2014

Example 3
three :: Integer -> Integer
three n = 3

Try to evaluate three infinity:

three infinity
3

Evaluation is trivial using lazy evaluation strategy


Cannot evaluate using strict evaluation strategy!
Guarantee: if both lazy and strict evaluations
terminate, they give the same result

Functional Programming: Spring 2014

Example 3
Recall that non-terminating expressions like
infinity are denoted by _|_ (bottom)
Definition of lazy/strict functions:

if f _|_ == _|_, the function is strict


otherwise (like three) the function is lazy

Strict functions require that their arguments be


evaluated before proceeding
Even Haskell has some strict functions

e.g. built-in arithmetic operations (+ - * / on Ints


etc.)

Functional Programming: Spring 2014

Example 4

Good old factorial:

factorial :: Int -> Int


factorial 0 = 1
factorial n = n * factorial (n - 1)

We will evaluate factorial 3


We'll assume that something needs this result,
otherwise it will just stay as (unevaluated)
factorial 3

Functional Programming: Spring 2014

Example 4

factorial 3

doesn't match factorial 0, continue


matches factorial n with n == 3
evaluate n * factorial (n 1) with n == 3
evaluate 3 * factorial (3 1)
* on Ints is strict in both arguments (built-in operator)
need to evaluate factorial (3 1)
pattern matching here requires that we evaluate
(3 1) so we can tell if this matches 0 or not
evaluate 3 1 2
Continued

Functional Programming: Spring 2014

Example 4

Continuing

evaluate factorial 2 2 * factorial (2 1)


Note: full expression now is:
3 * (2 * factorial (2 1))
evaluate 2 * factorial (2 1)
2 * factorial 1
2 * (1 * factorial (1 1))
2 * (1 * factorial 0)
Recall: factorial 0 reduces to 1
2 * (1 * 1)
Continuing

Functional Programming: Spring 2014

Example 4

Continuing

Recall pending operation:


3 * (2 * factorial (2 1))
3 * (2 * (1 * 1))
3 * (2 * 1)
3 * 2
6

Functional Programming: Spring 2014

Example 4

Notes on this example:

Most of it was very simple


Just high school algebra: substitute equals for equals,
simplify
Tricky parts:
Knowing which operators/functions are strict
e.g. factorial is strict in its argument

Knowing when evaluation must be forced

Lazy evaluation is one of the conceptually hardest


features of Haskell!

but can also be very useful


we'll see much more of this later in course

Functional Programming: Spring 2014

Example 5

Recall tail-recursive factorial function:

factorial_tr :: Int -> Int


factorial_tr n = iter n 1
where
iter :: Int -> Int -> Int
iter 0 r = r
iter n r = iter (n 1) (n * r)
Let's evaluate factorial_tr 3

Functional Programming: Spring 2014

Example 5
factorial_tr 3
iter 3 1
doesn't match iter 0 r, continue
matches iter n r with n == 3, substitute
iter (3 1) (3 * 1)
must reduce 3 1 to check pattern matching with 0
iter 2 (3 * 1)
doesn't match iter 0 r, continue
matches iter n r with n == 2, r = (3 * 1),
substitute
iter (2 1) (2 * (3 * 1))
Functional Programming: Spring 2014

Example 5

iter (2 1) (2 * (3 * 1))
must evaluate (2 1) for pattern matching
iter 1 (2 * (3 * 1))
iter (1 - 1) (1 * (2 * (3 * 1)))
must evaluate (1 1) for pattern matching
iter 0 (1 * (2 * (3 * 1)))
(1 * (2 * (3 * 1)))
(1 * (2 * 3))
(1 * 6)
6
Functional Programming: Spring 2014

Example 5

Note that iter is strict in its first argument only


Second argument not evaluated until iter is done and a
value result is needed
In fact, no computation at all is done unless the result is
needed!

So factorial_tr 3 won't be evaluated unless you need to do


something with the result (e.g. print it)

Probably not the way you're used to thinking about how


computations unfold

Functional Programming: Spring 2014

Next time
More Haskell basics
Lists
Polymorphic types
Function composition
Point-free and point-wise style

Functional Programming: Spring 2014

CS 115
Functional Programming
Lecture 3: April 9, 2014

Lists

Functional Programming: Spring 2014

Today
More Haskell basics
Polymorphic types
Lists
Functions on lists

Functional Programming: Spring 2014

More Haskell basics

Functional Programming: Spring 2014

Equality operators

Equality is tested using the == operator

Prelude> 1 == 2
False

Inequality is tested using the /= operator

Prelude> 1 /= 2
True

Functional Programming: Spring 2014

Equality operators

Equality/inequality works on values of the


same type only

Prelude> 1 /= 'c'
[type error]

Equality can work on many different kinds of


types

Prelude> 1 /= 2
True
Prelude> 'a' == 'a'
True
Functional Programming: Spring 2014

Equality operators

Type of equality operators:

Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool

The a -> a -> Bool part is an example of


a polymorphic type

A polymorphic type is parameterized on one or more


type variables (in this case a)
Recall that (concrete) type names need to be
capitalized
Type variable names start with lower-case letters

Functional Programming: Spring 2014

Equality operators
The a -> a -> Bool type states that any
equality function must take two arguments of
the same type (a) and return a Bool
The Eq a => part refers to a type class

later lecture!

Functional Programming: Spring 2014

Boolean operators/functions

Some boolean operators include:

(||) :: Bool -> Bool -> Bool


(&&) :: Bool -> Bool -> Bool

|| is logical or, && is logical and

Prelude> True || False


True
Prelude> True && False
False
not function works as expected

Functional Programming: Spring 2014

Negative numbers

Literal negative numbers sometimes need to


be surrounded by parentheses

Prelude> -10
-10
Prelude> 3 + -10
[precedence parsing error]
Prelude> 3 + (-10)
-7

Functional Programming: Spring 2014

Pattern guards
Pattern matching matches on structural
features of function arguments
Sometimes need to test for non-structural
features (arbitrary predicates)

abs :: Int -> Int


abs x | x < 0 = -x
abs x = x

The | x < 0 is a pattern guard

Functional Programming: Spring 2014

Pattern guards
abs x | x < 0 = -x

This says: "match the argument with x, as


long as x is less than zero"
With other equation, equivalent to:

abs x = if x < 0 then x else x

Basic structure:

<pattern> | <boolean expression> = ...

Where the <boolean expression> can


depend on names bound in the pattern

Functional Programming: Spring 2014

Multiple guards

Can have more than one guard for one


pattern:

abs x | x < 0 = -x
| x > 0 = x
| x == 0 = 0

Guards tried one after another until one works


Equivalent to:

abs x | x < 0 = -x
abs x | x > 0 = x
abs x | x == 0 = 0

Functional Programming: Spring 2014

Pattern wildcards

When you need to match something, but you


don't care what the matched value is, use a
wildcard pattern (_):

three :: a -> Int


three _ = 3

(Note the polymorphic type!)

Prelude> three 10
3
Prelude> three 'a'
3
Functional Programming: Spring 2014

Pitfall

This definition:

abs x | x < 0 = -x
| x > 0 = x
| x == 0 = 0

Causes ghci to complain:

Warning: Pattern match(es) are non-exhaustive


In an equation for `abs': Patterns not matched: _

What's the problem?

Functional Programming: Spring 2014

Pitfall
abs x | x < 0 = -x
| x > 0 = x
| x == 0 = 0

Mathematically, these cases are exhaustive


But Haskell doesn't "know" enough math to
check for exhaustive conditions in guards

Only checks for exhaustive patterns

Haskell can't prove that there is no x that


doesn't match any of the three guard clauses!

Functional Programming: Spring 2014

Pitfall

Fix:

abs x | x < 0 = -x
| x > 0 = x
| otherwise = 0

otherwise is just another name for True

Prelude> otherwise
True

Haskell knows that this will always match, so


cases are provably exhaustive

Functional Programming: Spring 2014

Negative numbers again

Try:

Prelude> abs
10
Prelude> abs
[nasty error
Prelude> abs

10
-10
message!]
(-10)

Haskell thinks that abs -10 is 10 subtracted


from the abs function!
Need parentheses to disambiguate

Functional Programming: Spring 2014

Note
ghc(i) doesn't check for exhaustive pattern
matches by default
To enable this, use the -W command-line
option, which enables warnings
% ghci W
[now warnings are enabled]

Functional Programming: Spring 2014

error

Some functions are conceptually partial

only defined over a subset of the type

Example: factorial not defined for negative


numbers:

factorial 0 = 1
factorial n = n * factorial (n 1)

What if we did this?


Prelude> factorial (-1)

Functional Programming: Spring 2014

error

One way to handle this is to make partial


functions total (handling all values in the input
types) by using the error function for
missing values:

factorial 0 = 1
factorial n | n < 0 = error "bad input"
factorial n = n * factorial (n 1)

This is a very crude form of error handling

alternatives exist (see later in course)

Functional Programming: Spring 2014

Lists

Functional Programming: Spring 2014

Lists
Lists (singly-linked lists) are a pervasive data type
in most functional programming languages
They are the default sequence type in many
applications
Haskell lists:

are immutable (cannot change values)


have only values of one type

As a result, lists can share structure without any


problems resulting

affects design of list processing procedures

Functional Programming: Spring 2014

Lists
There are a vast number of list processing
functions in the Haskell libraries
Many are so common that they are included in
the Prelude
Others are generally found in the Data.List
module

Functional Programming: Spring 2014

Data.List

To load the Data.List module into a ghci


session:

Prelude> :module +Data.List


Prelude Data.List>

Prompt changes to indicate new module


loaded
To unload:

Prelude Data.List> :module Data.List


Prelude>

(Can use :m as abbreviation for :module)

Functional Programming: Spring 2014

Data.List

To import the Data.List module into


another Haskell module:

import Data.List

This brings all the names in Data.List into


the local namespace
Can qualify imported names with either

import qualified Data.List


import qualified Data.List as L

First way adds Data.List. prefix, second


adds L. prefix (my preference)

Functional Programming: Spring 2014

Data.List

Another way to load the Data.List module


into a ghci session:

Prelude> import Data.List


Prelude Data.List>

But no unimport keyword, so to unimport


this module must still do this:

Prelude Data.List> :m Data.List

Functional Programming: Spring 2014

Lists

Lists have two fundamental operations:

and one fundamental datum:

construction
deconstruction (pattern matching)
the empty list ([])

With these, all list operations can be derived

Functional Programming: Spring 2014

List construction
Lists are constructed using the : (cons)
operator, a value, and a list of the same type
as the value
[] is a list
1 : [] is a list of Ints (say)
2 : (1 : []) is a list of Ints

can write it as 2 : 1 : [] since : operator


associates to the right

3 : 2 : 1 : [] is a list of Ints
Syntactic sugar: write as [3, 2, 1]

Functional Programming: Spring 2014

List construction

Let's ask ghci about the : operator:

Prelude> :type (:)


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

Note that : has a polymorphic type!

Prelude> :info (:)


data [] a = [] | a : [a]
infixr 5 :

Precedence 5, right-associative
We'll talk about data declaration next time

Functional Programming: Spring 2014

List construction

So : takes a value (of type a) and a list of


type a, and creates a longer list with the value
at the front of the list

Prelude> 1 : [2, 3, 4, 5]
[1, 2, 3, 4, 5]

Can even write as

Prelude> (:) 1 [2, 3, 4, 5]


[1, 2, 3, 4, 5]

though not obvious why you'd want to

Functional Programming: Spring 2014

List construction

Haskell lists are not heterogeneous!

This isn't Scheme!


Prelude> 'a' : [1, 2, 3]
[type error]
Prelude> [1, 2, 3] : [4, 5, 6]
[type error]

However, can define new data types to give


the effect of heterogeneous lists if you need to
(next lecture)

Functional Programming: Spring 2014

List deconstruction
In order to use lists, we have to be able to
"undo" the : operator to get the contents of
the list
In some languages, use head/tail functions
(or car/cdr for Schemers)

can do this in Haskell too

Normally, we use pattern matching to


deconstruct a list
What are the "natural" components of a list to
pattern-match against?

Functional Programming: Spring 2014

List deconstruction
Answer: head of list, and tail of list
Example function: length of a list

length :: [a] -> Int


length [] = 0
length (x:xs) = 1 + length xs

Note the (x:xs) on the left-hand side


The list argument is broken into the head (x)
and the tail (xs), both of which are in scope in
the right-hand side of the equation
Note the polymorphic type!

Functional Programming: Spring 2014

List deconstruction

Evaluate length [1, 2, 3]:

length [1, 2, 3]
length (1 : [2, 3])
-- def'n of :
-- match x with 1, xs with [2, 3]
1 + (length [2, 3])
-- eqn 2
1 + (length (2 : [3])) -- def'n of :
1 + (1 + (length [3])) -- eqn 2
1 + (1 + (length (3 : [])))
1 + (1 + (1 + (length [])))
1 + (1 + (1 + (0)))
-- eqn 1
3
Functional Programming: Spring 2014

List deconstruction

Alternate definition of length:

length :: [a] -> Int


length [] = 0
length x = 1 + length (tail x)

This is valid, but generally poor style to use


head or tail when you can use pattern
matching instead

Functional Programming: Spring 2014

List deconstruction

Recall original definition of length:

length [a] -> Int


length [] = 0
length (x:xs) = 1 + length xs

Note that x on LHS is never used

gives warnings from ghc with W

Better to use _ (wildcard):


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

Functional Programming: Spring 2014

Pattern matching again

More ways to pattern match over lists:

foo
foo
foo
foo
foo

:: [a] -> Int


[x, y, z] = 3 -- match 3-elem list
[1, 2] = 5
-- match only list [1, 2]
(x:y:z:rest) = 7
w = 9

Pattern matching on lists is very flexible!


(Would normally use _ for unused variables on lefthand side)

Functional Programming: Spring 2014

Some useful list functions

Functional Programming: Spring 2014

head and tail


Prelude> head [1, 2, 3]
1
Prelude> tail [1, 2, 3]
[2, 3]
Prelude> head []
*** Exception: Prelude.head: empty list

Again: do not use head or tail where pattern


matching is more natural!
Guess: types of head and tail

Functional Programming: Spring 2014

++ operator

List concatenation uses the ++ operator:

Prelude> [1, 2, 3] ++
[1,2,3,4,5,6]
Prelude> [] ++ [4, 5,
[4,5,6]
Prelude> [1, 2, 3] ++
[1,2,3]
Prelude> :info (++)
(++) :: [a] -> [a] ->
infixr 5 ++

[4, 5, 6]
6]
[]

[a]

Functional Programming: Spring 2014

++ operator

Pitfall: What's wrong with this definition?

last :: [a] -> a


last (_ ++ [x]) = x

Pattern matching will not work on arbitrary


operators! (Haskell isn't Prolog!)
Will only work on "data constructors" and some
built-in special cases, like : operator, tuples, and
literals like numbers, chars, and strings

Functional Programming: Spring 2014

++ operator

Definition of ++ operator:

(++) :: [a] -> [a] -> [a]


(++) [] ys = ys
(++) (x:xs) ys = x : (xs ++ ys)

Could also write like this:

[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)

Why not like this?

[] ++ ys = ys
(x:xs) ++ ys = [x] ++ (xs ++ ys)
Functional Programming: Spring 2014

concat
The concat function concatenates a list of lists
One definition:

concat :: [[a]] -> [a]


concat [] = []
concat (xs:xss) = xs ++ concat xss

We'll see a more elegant definition later

Functional Programming: Spring 2014

reverse
The reverse function reverses a list
One definition:

reverse :: [a] -> [a]


reverse [] = []
reverse (x:xs) = reverse xs ++ [x]

Any problems with this definition?


Efficiency of this function?

Functional Programming: Spring 2014

reverse

Alternate definition:

reverse :: [a] -> [a]


reverse xs = iter xs []
where
iter :: [a] -> [a] -> [a]
iter [] ys = ys
iter (x:xs) ys = iter xs (x:ys)
Efficiency?

Functional Programming: Spring 2014

reverse
iter :: [a] -> [a] -> [a]
iter [] ys = ys
iter (x:xs) ys = iter xs (x:ys)
iter is a tail-recursive function
"linear iterative" in CS 4 terminology
means: recursive call (tail call) has no pending operations
can be more space efficient in some circumstances
though lazy evaluation can have counterintuitive effects!
see this in assignment

Functional Programming: Spring 2014

take and drop

take takes a certain number of elements from the


front of a list:

Prelude> take 3 [1, 2, 3, 4, 5]


[1, 2, 3]

drop "drops" a certain number of elements from


the front of a list:

Prelude> drop 3 [1, 2, 3, 4, 5]


[4, 5]

Note: neither one changes the input list

Haskell doesn't allow this (no mutation)


Functional Programming: Spring 2014

The .. syntax

The .. syntax (not an operator!) constructs


enumerations:

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

Equivalent to enumFromTo 1 10

Prelude> [1,3..10]
[1,3,5,7,9]

Equivalent to enumFromThenTo 1 3 10

Functional Programming: Spring 2014

Infinite lists

Haskell, being lazy, has the notion of infinite lists:

Prelude> take 10 [1..]


[1,2,3,4,5,6,7,8,9,10]

[1..] is the list of all the positive integers!


Haskell generates 1 (the head) and knows how to
generate the rest as needed
What is the value of this?
Prelude> take 10 (drop 10 [1..])

Functional Programming: Spring 2014

Indexing: the !! operator


Indexing on lists is done with the !! operator:
Prelude> [1,2,3,4,5] !! 0
1
Prelude> [1,2,3,4,5] !! 4
5
This is rarely a good way to use lists

Functional Programming: Spring 2014

Indexing: the !! operator

Let's work through a definition:

(!!) :: [a] ->


[] !! _
_ !! n | n < 0
(x:_) !! 0
(_:xs) !! n

Int -> a
= error "invalid index"
= error "invalid index"
= x
= xs !! (n 1)

Efficiency?
A list is not an array; don't use it like one

Functional Programming: Spring 2014

takeWhile and dropWhile


takeWhile takes elements from the front of a list
as long as some criterion is met
dropWhile drops elements from the front of a list
as long as some criterion is met

Prelude> takeWhile (>0) [1, 2, 3, -1, -2]


[1, 2, 3]
Prelude> dropWhile (==0) [0, 0, 0, 1, 2]
[1, 2]

Functional Programming: Spring 2014

zip and unzip


zip takes two lists and "zips" them together into a
list of two-tuples
unzip takes a list of two-tuples and "unzips" them
into two lists (two-tuple of two lists)

Prelude> zip [1, 2, 3] [4, 5, 6]


[(1, 4), (2, 5), (3, 6)]
Prelude> unzip [(0, 1), (2, 3), (4, 5)]
([0,2,4], [1,3,5])

Functional Programming: Spring 2014

zipWith

zipWith is like zip, except that it applies a twoargument function to the two-tuples

Prelude> zip [1, 2, 3] [4, 5, 6]


[(1, 4), (2, 5), (3, 6)]
Prelude> zipWith (+) [1, 2, 3] [4, 5, 6]
[5,7,9]

Note: zipWith is a higher-order function

takes a function (operator) as its argument


the (op) syntax converting an operator to a function is
essential here
Functional Programming: Spring 2014

and and or

and and or are multi-argument generalizations of


the && and || operators

Prelude> :t and
[Bool] -> Bool
Prelude> and [True, True, False]
False
Prelude> :t or
[Bool] -> Bool
Prelude> or [False, False, True]
True
Functional Programming: Spring 2014

Next time
More on list functions
Higher-order list functions: map, filter,
foldr and friends
List comprehensions

Functional Programming: Spring 2014

CS 115
Functional Programming
Lecture 4: April 11, 2014

Higher-order functions, part 1

Functional Programming: Spring 2014

Today
Higher-order functions on lists
Anonymous functions
Simple code transformations
Point-free style
More on lazy evaluation

Functional Programming: Spring 2014

Higher-order functions

Functions in functional languages are data

can be passed as arguments to other functions


can be created on-the-fly
can be returned from functions

Functions which take other functions as


arguments and/or return functions as
results are called "higher-order" functions

Functional Programming: Spring 2014

Higher-order functions
Higher-order functions are the first distinctly
"functional" aspect of functional programming
we've seen
This will lead to a style of programming where
new functions can often be created by
"snapping together" other functions
Benefits:

easier to write code


greater confidence that the code is correct

Functional Programming: Spring 2014

List functions
Functions on lists include many very useful
higher-order functions
Two simple examples: map and filter

Functional Programming: Spring 2014

map

The map function takes a function and a list


as its arguments

applies the function to each element of the list


collects all the results in a new list
returns the new list

For this to work, the function must be unary


(taking one argument)
Fortunately, all Haskell functions are unary,
so not a limitation J

Functional Programming: Spring 2014

map
What is map's type signature?
map :: (a -> b) -> [a] -> [b]
map takes

a function from type a to type b


a list of values of type a

and returns

a list of values of type b


Note: type b can be the same as type a

Functional Programming: Spring 2014

map examples
double :: Int -> Int
double x = 2 * x
Prelude> map double [1, 2, 3, 4, 5]
[2,4,6,8,10]
Prelude> map (*2) [1, 2, 3, 4, 5]
[2,4,6,8,10]

Functional Programming: Spring 2014

Anonymous functions
Often we want to create a function for a
single use
For example: double function on previous
slide

maybe that's the only place double was ever


needed
requiring that you write a separate function for
this is overkill
can use an operator section like (2*) here, but
this is not always possible

Functional Programming: Spring 2014

Anonymous functions
Haskell allows you to define anonymous
functions (functions with no name)
This is part of what it means for functions
to be data: can create them "on the fly"
They are also referred to as lambda
expressions

from lambda calculus (theoretical underpinnings


of Haskell) and Lisp/Scheme

Functional Programming: Spring 2014

Anonymous functions

Example:

Prelude> map (\x -> 2 * x) [1, 2, 3, 4, 5]


[2, 4, 6, 8, 10]

Syntax:

\<pattern> -> <expression>

Often <pattern> is one or more variables


The \ is the typographic symbol most similar to
"lambda" ()

Functional Programming: Spring 2014

Puzzle

What does this return?

Prelude> map (\x y -> x + y) [1, 2, 3, 4, 5]


Hint: the lambda expression (\x y -> x + y) has
the type (Int -> Int -> Int) (when used with

Ints)
This expression returns a list of type [Int -> Int]
It's a list of adder functions:
[\y -> 1 + y, \y -> 2 + y, \y -> 3 + y,
\y -> 4 + y, \y -> 5 + y]
Equivalent to e.g. [add1, add2, add3, add4,
add5]
Functional Programming: Spring 2014

Puzzle

Useful way to think about this:

\x y -> x + y

is the same as:

\x -> \y -> x + y

(due to currying)

Functional Programming: Spring 2014

Puzzle

This is equivalent to

map (+) [1, 2, 3, 4, 5]

This looks like a type error, but isn't due to curried


nature of functions
Note: if you type this into ghci, get error because
can't print functions (no printable representation)

Functional Programming: Spring 2014

Definition of map
map :: (a -> b) -> [a] -> [b]
map f [] = [] -- or use _ for f
map f (x:xs) = f x : map f xs

Functional Programming: Spring 2014

Infinite lists and map

This definition works with infinite lists:

Prelude> take 10 (map (2*) [1..])


[2,4,6,8,10,12,14,16,18,20]

Interesting definition:

integers :: [Integer]
integers = 1 : map (1+) integers

Let's evaluate:

take 3 integers
Functional Programming: Spring 2014

Infinite lists and map


take definition:
take :: Int -> [a] -> [a]
take 0 _ = []
take n [] = error "take: no elements"
take n (x:xs) = x : take (n-1) xs

N.B. This isn't exactly the same as the built-in take function

Evaluate:

take 3 integers

Functional Programming: Spring 2014

Infinite lists and map

Evaluate:

take 3 integers

Reduce:

take 3 (1 : map (1+) integers)


1 : take 2 (map (1+) integers)
1 : take 2 (map (1+) (1 : map (1+) integers))
1 : take 2 (2 : map (1+) (map (1+) integers))
1 : 2 : take 1 (map (1+) (map (1+) integers))
1 : 2 : take 1 (map (1+) (map (1+) (1 : map (1+) integers)))
1 : 2 : take 1 (map (1+) (2 : map (1+) (map (1+) integers)))
1 : 2 : take 1 (3 : map (1+) (map (1+) (map (1+) integers)))

Functional Programming: Spring 2014

Infinite lists and map

Continue:

1 : 2 : take 1 (3 : map (1+) (map (1+) (map (1+) integers)))


1 : 2 : 3 : take 0 (map (1+) (map (1+) (map (1+) integers)))
1 : 2 : 3 : []
[1, 2, 3]

Answer: [1, 2, 3]
Note that

map (1+) (map (1+) (map (1+) integers))

was never calculated (lazy evaluation)

Functional Programming: Spring 2014

filter

filter is a higher-order function which takes as its


arguments

a predicate (function returning a Bool)


a list

and returns a list of the elements in the original list


that the predicate returned True on (in the same
order)
Type signature:
filter :: (a -> Bool) -> [a] -> [a]

Functional Programming: Spring 2014

filter

Definition of filter:

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


filter _ [] = []
filter p (x:xs) | p x = x : filter p xs
| otherwise = filter p xs

Functional Programming: Spring 2014

filter

Examples of filter:

Prelude> filter (\x -> x `mod` 2 == 1) [1..10]


[1,3,5,7,9]
Prelude> filter (/= 0) [0, 1, 0, 2, 0, 3, 0]
[1,2,3]

filter works fine on infinite lists:

Prelude> take 3 (filter (\x -> x `mod` 2 == 1) [1..])


[1,3,5]

Exercise: work through this evaluation!

Functional Programming: Spring 2014

map and filter


map and filter: two great tastes that taste great
together!
Consider:

twiceNonzeros
twiceNonzeros
twiceNonzeros
twiceNonzeros

:: [Int] -> [Int]


[] = []
(0:xs) = twiceNonzeros xs
(x:xs) = 2 * x : twiceNonzeros xs

This definition is correct, but uses explicit recursion


It's considered poor style to define like this if we can
define it without explicit recursion

Functional Programming: Spring 2014

map and filter

New definition:

twiceNonZeros xs = map (2*)


(filter (\x -> x /= 0) xs)

Much nicer!

Functional Programming: Spring 2014

The $ operator
twiceNonzeros :: [Int] -> [Int]
twiceNonZeros xs = map (2*)
(filter (\x -> x /= 0) xs)

There are a couple of simple improvements we can


make to this code
We can get rid of the last set of parentheses using
the $ (apply) operator:

twiceNonZeros xs =
map (2*) $ filter (\x -> x /= 0) xs

Functional Programming: Spring 2014

The $ operator
Prelude> :info $
($) :: (a -> b) -> a -> b
infixr 0 $

This operator takes a function (from a to b) and a


value of type a, and applies the function to the value
to get a return value of type b
Just an operator version of function application
Why bother using this?

f $ x is just the same as f x

The answer is in the infixr 0 $ part


Functional Programming: Spring 2014

The $ operator
The $ operator has the lowest possible precedence,
so anything on its right-hand side gets evaluated
before the function is applied
Use case: consider a chain of function applications:
f1 (f2 (f3 (f4 (f5 x))))
Using $ makes this cleaner:
f1 $ f2 $ f3 $ f4 $ f5 x
$ associates to the right so it's equivalent to:
f1 $ (f2 $ (f3 $ (f4 $ (f5 x))))
Which is the same as the expression without $

Functional Programming: Spring 2014

Function composition
twiceNonZeros xs =
map (2*) $ filter (\x -> x /= 0) xs

Even this can be improved!


This function is just the composition of two smaller
functions:

map (2*) :: [Int] -> [Int]


filter (\x -> x /= 0) :: [Int] -> [Int]

Haskell has a function composition operator: the dot


(.)

used everywhere in Haskell code!


Functional Programming: Spring 2014

Function composition
Prelude> :info (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
infixr 9 .

Definition:
f . g = \x -> f (g x)
With this, our function now becomes:
twiceNonZeros =
map (2*) . filter (\x -> x /= 0)
We got rid of the function's argument!

Functional Programming: Spring 2014

Function composition
twiceNonZeros =
map (2*) . filter (\x -> x /= 0)
The . operator has very high precedence (9) but
function application is still higher, so we don't have to
write this as:
twiceNonZeros =
map (2*) . (filter (\x -> x /= 0))
Haskell makes it very convenient to define functions
by composing other functions

Functional Programming: Spring 2014

Function composition
twiceNonZeros =
map (2*) . filter (\x -> x /= 0)
But wait! We can improve this still more!
Can rewrite (\x -> x /= 0) as an operator
section!
twiceNonZeros = map (2*) . filter (/= 0)
Compare to:
twiceNonzeros [] = []
twiceNonzeros (0:xs) = twiceNonzeros xs
twiceNonzeros (x:xs) = 2 * x : twiceNonzeros xs

Functional Programming: Spring 2014

Function composition
Advantages of:
twiceNonZeros = map (2*) . filter (/= 0)
Much shorter! (Less code to get wrong)
Two actions (mapping and filtering) are separated
instead of interleaved together
Easier to write, easier to understand what's going on
Now you see why explicit recursion is frowned upon
in Haskell

Functional Programming: Spring 2014

(Aside) Eta equivalence

In Haskell, these two expressions are equivalent:

\x -> f x
f

Theorists say that they are eta-equivalent


Going from \x -> f x to just f is called an etareduction
Going from f to \x -> f x is an eta-expansion
Eta equivalence doesn't always hold in strict
languages!

Just f might have to be evaluated in some context where


\x -> f x would not require f to be evaluated yet
Functional Programming: Spring 2014

(Aside) Eta equivalence


We can sometimes use eta equivalence to simplify
function definitions
For instance, if we had written our previous function
as:

twiceNonZeros xs =
(map (2*) . filter (/= 0)) xs

Eta-equivalence says we can drop the xs from both


sides
This is (usually) considered good style

Functional Programming: Spring 2014

(Aside) Eta equivalence

However, we can't change:

twiceNonZeros xs =
map (2*) $ filter (/= 0) xs

Into:

twiceNonZeros =
map (2*) $ filter (/= 0)

Why not?

Functional Programming: Spring 2014

(Aside) Eta equivalence

Reason:

twiceNonZeros xs =
map (2*) $ filter (/= 0) xs

Can't be written as:

twiceNonZeros xs =
(map (2*) $ filter (/= 0)) xs

So eta-equivalence doesn't apply


Yet another reason to prefer the version using
function composition!

Functional Programming: Spring 2014

Point-free style
The style of defining functions by composing
together a bunch of smaller functions, without writing
out the arguments, is called point-free style
Can often make code much more elegant and
concise
Occasionally can make code so "tight" it's hard to
read/understand
Use your own coding judgment!

Functional Programming: Spring 2014

Point-free style

Without point-free style (AKA point-wise style) you


might have e.g.:

-- want to build function q out of


-- functions f, g, h
q x = let x1 = f x in
let x2 = g x1 in
let x3 = h x2 in
x3

All arguments ("points") are explicitly named: x, x1,


x2, x3
Functional Programming: Spring 2014

Point-free style

With point-free style this is just

-- want to build function q out of


-- functions f, g, h
q = h . g . f

Much simpler!
NOTE: the "point" of "point-free style" does not refer
to the function composition (.) operator!
Point-free style has no (or at least fewer)
"points" (explicit names for function arguments) but
more "dots" (function composition operators)

Functional Programming: Spring 2014

Efficiency
Our previous function has been reduced to:
twiceNonZeros = map (2*) . filter (/= 0)
Elegance/clarity advantages are obvious
But what about efficiency?
Consider applying this function to a list of
1,000,000,000 Ints, about 20% of which are zeros
Can you imagine any possible problems with this?

Functional Programming: Spring 2014

Efficiency
twiceNonZeros = map (2*) . filter (/= 0)
The evaluation strategy is important here
In a strict language, might have to create a
temporary list to hold the filtered data (80% as long
as original data), then map (2*) over that to get
final list
A huge amount of extra memory required!
But in a lazy language like Haskell, this problem
doesn't come up
Let's work through an evaluation J
Functional Programming: Spring 2014

Efficiency
twiceNonZeros = map (2*) . filter (/= 0)
Evaluate twiceNonZeros [1, 0, 2, 0, 3, 0]
First few steps:
twiceNonZeros [1, 0, 2, 0, 3, 0]
(map (2*) . filter (/= 0)) [1, 0, 2, 0, 3, 0]
map (2*) (filter (/= 0) [1, 0, 2, 0, 3, 0])
map (2*) (1 : filter (/= 0) [0, 2, 0, 3, 0])

What is the next step?

Functional Programming: Spring 2014

Efficiency
map (2*) (1 : filter (/= 0) [0, 2, 0, 3, 0])
2 : (map (2*) (filter (/= 0) [0, 2, 0, 3, 0]))

The filtering hasn't completed, but due to lazy


evaluation we're already doing the mapping!
For instance, what if the original expression had
been

Prelude> head $ twiceNonZeros [1, 0, 2, 0, 3, 0]


2

We could stop now!


Items are computed on demand

Functional Programming: Spring 2014

Efficiency
Items are computed on demand
What does this mean?
Any function that "consumes" the list returned from
the map/filter is going to want to first look at the head
of the list
So the head of the list is the first thing that gets
computed
The computation runs so as to first generate the
head of the list, then the next item, and so on
Items are computed one at a time

Functional Programming: Spring 2014

Efficiency

Continuing

2
2
2
2
2
2
2
2
2
2

:
:
:
:
:
:
:
:
:
:

(map (2*) (filter (/= 0) [0, 2, 0, 3, 0]))


(map (2*) (filter (/= 0) [2, 0, 3, 0]))
(map (2*) (2 : filter (/= 0) [0, 3, 0]))
4 : (map (2*) (filter (/= 0) [0, 3, 0]))
4 : (map (2*) (filter (/= 0) [3, 0]))
4 : (map (2*) (3 : filter (/= 0) [0]))
4 : 6 : (map (2*) (filter (/= 0) [0]))
4 : 6 : (map (2*) (filter (/= 0) []))
4 : 6 : (map (2*) [])
4 : 6 : []

[2, 4, 6]

Functional Programming: Spring 2014

Efficiency
Note that operations of mapping and filtering are
automatically interleaved by lazy evaluation, even
though code doesn't do that explicitly
Consequence: code doesn't have to generate large
intermediate lists: huge space savings!
In strict language, might have to interleave the
operations explicitly (like in first version of
twiceNonZeros) to get space efficiency
Conclusion: lazy evaluation improves modularity!

Functional Programming: Spring 2014

Classic paper
Why Functional Programming Matters by John
Hughes
Explores consequences of lazy evaluation for
modularity
Uses a language called Miranda, which is very
similar to Haskell (an ancestor language)

you should be able to follow it

He argues that lazy evaluation provides "better glue"


to connect independent pieces of programs together
to form new programs
Functional Programming: Spring 2014

Next time
More higher-order list functions: foldr
and friends
List comprehensions

Functional Programming: Spring 2014

CS 115
Functional Programming
Lecture 5: April 14, 2014

Higher-order functions, part 2

Functional Programming: Spring 2014

Today

More Haskell basics

More higher-order functions on lists

@-patterns, case expressions


foldr
foldl

List comprehensions

Functional Programming: Spring 2014

More Haskell Basics

Functional Programming: Spring 2014

@-patterns

Sometimes, when pattern matching an argument,


want to destructure the argument but also have a
name for the entire argument

so can use both in the definition

Functional Programming: Spring 2014

@-patterns

Example problem from assignment:

insert
insert
insert
insert

:: Integer -> [Integer] -> [Integer]


n [] = [n]
n m@(m1:_) | n < m1 = n : m
n (m1:ms) = m1 : insert n ms

In second equation, need both the parts of the list


(here, just the head m1) and the entire list m
The @-pattern says that m is the name of the entire
list and m1 is the name of the head

Functional Programming: Spring 2014

@-patterns

Without @-pattern we would have to write:

insert
insert
insert
insert

Problem: we are re-creating a list (m1:ms) which


already exists as m!

:: Integer -> [Integer] -> [Integer]


n [] = [n]
n (m1:ms) | n < m1 = n : m1 : ms
n (m1:ms) = m1 : insert n ms

Inefficient, poor style

Use @-patterns for more elegant code


Functional Programming: Spring 2014

case expression
Most often, we use pattern matching in equations
that define a function
We can also "manually" invoke pattern matching
using a case expression
All functions can be written using case expressions
instead of equations
Haskell compilers internally convert equations into
case expressions

Functional Programming: Spring 2014

case expression

Familiar example:

factorial :: Integer -> Integer


factorial 0 = 1
factorial n = n * factorial (n 1)

With case expression:

factorial n =
case n of
0 -> 1
m -> m * factorial (m 1)
Functional Programming: Spring 2014

case expression
When should we use case expressions?
Usually not good style to use when we can use
normal equational style instead (at top level)
Need to use a case expression if you have to
pattern-match against a value which has been
computed in the course of the execution of a
function
Also useful if need a where clause that spans
multiple cases

Functional Programming: Spring 2014

More higher-order functions

Functional Programming: Spring 2014

So far
We have seen the map and filter higher-order
functions
These functions take in and return lists
Sometimes want to take in an entire list and return a
single value that represents something interesting
about the list
We say that we want to "reduce" or "fold" the list into
a single value
Various Haskell functions exist to do this

Functional Programming: Spring 2014

Problem
Let's write a function to add up all the numbers in a
list of Integers
Writing a recursive definition is straightforward:

sum :: [Integer] -> Integer


sum [] = 0
sum (n:ns) = n + sum ns

Let's try to extract the relevant parts of this into a


higher-order function on lists
What aspects of this are likely to change?

Functional Programming: Spring 2014

Generalizing sum
The operation to be done on the list elements
doesn't have to be +
The value returned from the empty list doesn't have
to be 0
Let's write a more generic version of sum that can
take + and 0 as arguments
We'll call it accumulate

Functional Programming: Spring 2014

Generalizing sum

First try:

accumulate :: (Integer -> Integer -> Integer)


-> Integer -> [Integer] -> Integer
accumulate _ init [] = init
accumulate f init (n:ns) =
f n (accumulate f init ns)

Note that the structure of this function is identical to


the sum function, with sum replaced by
accumulate f init, + replaced by f, and 0
replaced by init
Functional Programming: Spring 2014

Generalizing sum even more

Arguments don't have to use Integer; let's make


this polymorphic!

accumulate :: (a -> a -> a) -> a -> [a] -> a


accumulate _ init [] = init
accumulate f init (n:ns) =
f n (accumulate f init ns)

Now we can accumulate any data type


But wait: why does the initial value have to be the
same type as the list of input values?

Functional Programming: Spring 2014

Generalizing sum even more

We can use (possibly) different types for initial value


and list of values (more general):

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


accumulate _ init [] = init
accumulate f init (n:ns) =
f n (accumulate f init ns)

This very general function has a name in Haskell:


foldr (for "fold right")
Let's see how to define sum in terms of foldr

Functional Programming: Spring 2014

Generalizing sum even more

sum in terms of foldr:

sum :: [Integer] -> Integer


sum lst = foldr (+) 0 lst

We can also write this as follows (eta contraction,


more point-free style):

sum :: [Integer] -> Integer


sum = foldr (+) 0
-- no lst on either side

Functional Programming: Spring 2014

Generalizing sum even more

Let's expand this definition using the equations for


accumulate (foldr)

sum [] = foldr (+) 0 [] = 0


sum (n:ns) = foldr (+) 0 (n:ns)
= n + (foldr (+) 0) ns

Substituting sum for foldr (+) 0, we get:

sum [] = 0
sum (n:ns) = n + sum ns

We've derived our original function!


Functional Programming: Spring 2014

foldr
What does foldr actually do?
Consider a list: [1, 2, 3, 4, 5]
Can write it as: 1 : 2 : 3 : 4 : 5 : []
Which really means:

foldr takes two arguments besides the list:

(1 : (2 : (3 : (4 : (5 : [])))))
a function of two arguments
an initial value

How does foldr use these to compute its value?


Functional Programming: Spring 2014

foldr
What does foldr actually do?
foldr takes

an operator (op)
an initial value (init)
a list

and returns the result of exchanging op for the :


operator (used to construct the list), and init for
the empty list

Functional Programming: Spring 2014

foldr
foldr (+) 0 [1, 2, 3, 4, 5]
foldr (+) 0 (1 : (2 : (3 : (4 : (5 : [])))))

Substitute + for :, 0 for [] to get:

1 +
1 +
1 +
1 +
1 +
15

(2
(2
(2
(2
14

+
+
+
+

(3 + (4 + (5 + 0)))))
(3 + (4 + 5))))
(3 + 9)))
12)

Functional Programming: Spring 2014

foldr
foldr is a very flexible function
Many other Haskell functions can be defined in
terms of foldr
Let's see some examples

Functional Programming: Spring 2014

concat
concat is a function which takes a list of lists and
concatenates it into a single list ("flattening" the list
of lists)
We can define concat recursively as follows:
concat :: [[a]] -> [a]
concat [] = ?
concat (xs:xss) = ?

Functional Programming: Spring 2014

concat
concat :: [[a]] -> [a]
concat [] = []
concat (xs:xss) = xs ++ (concat xss)
In terms of foldr:
concat xss = foldr (++) [] xss
or just:
concat = foldr (++) []

Functional Programming: Spring 2014

concat
Why this works:
concat [lst1, lst2, ...]
= lst1 ++ lst2 ++ ... ++ []

Functional Programming: Spring 2014

++
Can also define ++ (list append) in terms of foldr
Let's try to derive it:
[1, 2, 3] ++ [4, 5, 6]
(1 : (2 : (3 : []))) ++ [4, 5, 6]
foldr (:) [4, 5, 6] (1 : (2 : (3 : [])
Replace : with :, [] with [4, 5, 6] to get:
(1 : (2 : (3 : [4, 5, 6])))
[1, 2, 3, 4, 5, 6]

Functional Programming: Spring 2014

++
So we have this definition:
(++) :: [a] -> [a] -> [a]
lst1 ++ lst2 = foldr (:) lst2 lst1
Let's look at an even more elegant (point-free)
definition of ++
Rewrite definition as:
(++) lst1 lst2 = foldr (:) lst2 lst1

Functional Programming: Spring 2014

++
(++) lst1 lst2 = foldr (:) lst2 lst1
We would like to define ++ as:
(++) = foldr (:)
But this isn't correct (arguments lst1 and lst2 are
in wrong order)
Introduce the flip function:
flip :: (a -> b -> c) -> (b -> a -> c)
flip f x y = f y x

Functional Programming: Spring 2014

++

This leads to this definition:

(++) = flip (foldr (:))

and concat can be defined as:


concat = foldr (flip (foldr (:))) []

foldr is powerful!
Warning: excessive use of point-free style can lead
to impossible-to-understand code!

but it'll be really elegant J

Functional Programming: Spring 2014

Tip on using foldr

The function argument of foldr takes two


arguments:

the current element of the list


the result of applying foldr to the rest of the list

I sometimes write the function as (\x r -> ...)


where x is the current element, r the rest of the list
(after processing by foldr) to keep this straight

Functional Programming: Spring 2014

map in terms of foldr


Let's try to define map in terms of foldr
The value we're accumulating is the mapped list
We will end up with
map f lst = foldr (\x r -> ...) [] lst
Just need to fill in ...
Assume that r is the rest of the list, with f mapped
over it

i.e. r is map f (tail lst)

Then, how to define (\x r -> ...)?


Functional Programming: Spring 2014

map in terms of foldr


map f lst = foldr (\x r -> ...) [] lst
Assume that r is the rest of the list, with f mapped
over it

i.e. r is map f (tail lst)

(\x r -> ...) must be (\x r -> f x : r) to


get the entire mapped list
Definition:

map f lst = foldr (\x r -> f x : r) [] lst


map f = foldr (\x r -> f x : r) []
-- better
Functional Programming: Spring 2014

map in terms of foldr

Definition:

map f = foldr (\x r -> f x : r) []

Even more concise (almost) point-free definition:

map f = foldr ((:) . f) []

(Work out how this works.)


Totally pointfree definition:

map = flip foldr [] . ((:) .)

Awesome! J

Functional Programming: Spring 2014

foldl
We've seen that foldr can be used to represent
computations of this form:
x1 op (x2 op (x3 op (x4 op init)))
This is natural for operators that associate to the
right (like (:))
It would be nice to have a fold that can represent
computations of this form:
((((init op x1) op x2) op x3) op x4)
This is called a "left fold" or foldl

Functional Programming: Spring 2014

foldl
foldl combines the init value with the first list
element, and keeps combining with each successive
list value until the end of the list is reached
Definition:

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


foldl _ init [] = init
foldl f init (x:xs) = foldl f (f init x) xs

Functional Programming: Spring 2014

foldl

Definition:

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


foldl _ init [] = init
foldl f init (x:xs) = foldl f (f init x) xs

Thoughts on this? Advantages vs foldr?


Disadvantages?
Consider:
sum = foldl (+) 0

Functional Programming: Spring 2014

foldl
sum = foldl (+) 0
This is a valid definition of sum
foldl is tail-recursive ("iterative") so might
consume less space than foldr version

This is the case in strict languages


In lazy languages, not so simple (see assignment)

foldl can't be used on infinite lists, foldr


sometimes can

Functional Programming: Spring 2014

List comprehensions

Functional Programming: Spring 2014

List comprehensions
Often the case that you want to build a list with
certain properties
Elements of list are the result of evaluating an
expression for certain values of the variables in the
expression
May want to impose some other criteria on the list
elements as well
In Haskell, we can do this with a list comprehension

Functional Programming: Spring 2014

List comprehensions

A list comprehension is a description of

the elements of a list (expression)


where the variables come from (generators)
what other criteria must be met (filters)

Syntax:
[<expression> | <generators>, <filters>]
Generators have the form
x <- [1..1000]
(x is taken elementwise from the list [1..1000])

Functional Programming: Spring 2014

List comprehensions

Simple list comprehension:

Prelude> [x * 2 | x <- [1..10]]


[2,4,6,8,10,12,14,16,18,20]

Two generators:

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


[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),
(3,2),(3,3)]

Note: rightmost generator "changes fastest"

Functional Programming: Spring 2014

Filters

To filter out elements from a list comprehension, add


a boolean expression (which must evaluate to True
for the generator values to be accepted)

Prelude> [(x, y) | x <- [1..6], y <- [1..6],


x + y == 7]
[(1,6),(2,5),(3,4),(4,3),(5,2),(6,1)]

Can have multiple filters, separated by commas


Can interleave generators and filters

but filters must not refer to generators that follow them

Functional Programming: Spring 2014

Patterns in generators

Generators can bind patterns

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


[3,7,11]

Here, (1, 2) unpacked into (x, y), binding 1 to x


and 2 to y in the expression x + y

Functional Programming: Spring 2014

map, filter
List comprehensions can take the place of map:
map f [x1, x2, x3, ...]
== [f x | x <- [x1, x2, x3, ...]]
List comprehensions can take the place of filter:
filter p [x1, x2, x3, ...]
== [x | x <- [x1, x2, x3, ...], p x]

Functional Programming: Spring 2014

Examples

List comprehensions can be used to write very


concise definitions

quicksort :: [Integer] -> [Integer]


quicksort [] = []
quicksort (x:xs) =
quicksort lt ++ [x] ++ quicksort ge
where
lt = [y | y <- xs, y < x]
ge = [y | y <- xs, y >= x]

Functional Programming: Spring 2014

Next time
Defining new data types (algebraic data types)
Coming soon: Type classes

Functional Programming: Spring 2014

CS 115
Functional Programming
Lecture 6: April 16, 2014

Algebraic Datatypes

Functional Programming: Spring 2014

Today
Defining new datatypes in Haskell
Enumeration-style datatypes
Datatypes with arguments
Constructor functions
Record syntax
Recursive and polymorphic datatypes
Type synonyms
newtype

Functional Programming: Spring 2014

Bool
Consider the humble boolean type Bool
It has two possible values: True and False
The datatype Bool is thus a finite enumeration of
these two values
In fact, the Bool type is not "hard-wired" into
Haskell; it is defined in the Haskell libraries as a new
type definition

Functional Programming: Spring 2014

data
New datatypes are defined in Haskell with the data
declaration
Definition of Bool:
data Bool = True | False
The vertical bar | is used to separate alternative
values of the datatype
The name of the datatype (Bool) must begin with a
capital letter

Functional Programming: Spring 2014

data
data Bool = True | False
The two values True and False are constructors of
the data
Constructors also have to have names beginning
with a capital letter
Now Bool is a type just like Integer or Char, and
True and False are values just like 0 or 'a'
True and False are the only valid Bool values

Functional Programming: Spring 2014

ghci

Can write data declarations in ghci

(but only in most recent versions)

Let's define Bool ourselves:

Prelude> import Prelude hiding (Bool(..))


Prelude> True
[error message]
Prelude> data Bool = True | False deriving
(Show)
Prelude> True
True
Functional Programming: Spring 2014

ghci
Prelude> :info Bool
data Bool = True | False
instance Show Bool
(explained in later lecture)
Prelude> :type True
True :: Bool
Prelude> :type False
False :: Bool

Functional Programming: Spring 2014

Pattern matching
Defining a new datatype with a data declaration not
only defines a new type and new values of that type
Also allows you to pattern-match against those
values
Example: not function:
not :: Bool -> Bool
not True = False
not False = True
True/False on LHS are patterns (trivial patterns)

Functional Programming: Spring 2014

Aside: strictness
A Haskell function can be lazy or strict in any of its
arguments
If it's strict in an argument, then evaluating a nonterminating expression in that argument position will
force the entire function call to not terminate
We represent non-termination by the _|_ (bottom)
value (not Haskell syntax)
So a strict argument in function f has f _|_ = _|_

Functional Programming: Spring 2014

Example: || function
Logical or function uses the || operator
One definition:

(||) :: Bool -> Bool -> Bool


True || True = True
True || False = True
False || True = True
False || False = False

Is this definition lazy or strict in either argument?

Functional Programming: Spring 2014

Example: || function
(||) :: Bool -> Bool -> Bool
True || True = True
True || False = True
False || True = True
False || False = False

To pattern-match a boolean expression against


either True or False, it must be evaluated
completely
Therefore, this definition is strict in both arguments

Functional Programming: Spring 2014

Example: || function

Alternative definition of ||:

(||) :: Bool -> Bool -> Bool


True || _ = True
False || x = x

Is this definition lazy or strict in either argument?


Clearly strict in first argument (must evaluate to
True or False to do pattern matching)
Lazy in second argument (1st equation: don't even
need to evaluate 2nd argument)

Functional Programming: Spring 2014

Example: || function
Moral: Just because Haskell is a "lazy language"
doesn't mean that all functions are lazy in all
arguments
Structure of function may dictate that certain
arguments will always have to be evaluated all the
way, others not

Functional Programming: Spring 2014

Sum types
Many datatypes in Haskell are a series of
alternatives
Each constructor (like True or False) represents
one possible kind of data value of that type
These are sometimes called "sum types" (the type as
a whole is the "sum" of several disjoint constructors)
Sum types where the constructors have no
arguments are simply enumerations

Functional Programming: Spring 2014

More data examples

Easy to define enumeration-style datatypes:

data
data
data
data

Color
Day
Beatle
Major

=
=
=
=

Red | Green | Blue | Yellow


Mon | Tue | Wed | Thurs | Fri | Sat | Sun
John | Paul | George | Ringo
CS | Whatever

etc.
All such definitions also define pattern-matching on
the corresponding datatypes

Functional Programming: Spring 2014

More data examples

Example with Color:

data Color

opposite
opposite
opposite
opposite
opposite

= Red | Green | Blue | Yellow

:: Color
Red
=
Green =
Blue
=
Yellow =

-> Color
Green
Red
Yellow
Blue

Functional Programming: Spring 2014

Beyond enumerations
Enumeration types are trivial, but still more pleasant
and well-typed than e.g. C-style enum values
(aliases for integers)
However, data declarations can do much more:

constructors can have one or more arguments (even of


same type as the type being defined!)
different constructors don't have to have the same number
of arguments

Functional Programming: Spring 2014

Product types
Some datatypes consist of a single constructor which
has one or more arguments
This corresponds to what is usually called a "record"
or "struct" in other languages

also similar to a Haskell tuple, but with a label

Technically, these are called "product types" because


the type is the Cartesian product of the types of the
arguments to the constructor (where types
conceptually represent sets of values)
Functional Programming: Spring 2014

Product types

Examples:

data Point = Pt Double Double


data Person = Per String String
data Course = C String Int

-- x and y coords
-- first and last names
-- field, number

Some programmers reuse the type name as the


constructor name (legal in Haskell):

data Point = Point Double Double


data Person = Person String String
data Course = Course String Int

Functional Programming: Spring 2014

Product types

Again, product types define pattern-matching over


their constructors:

distance :: Point -> Point -> Double


distance (Pt x1 y1) (Pt x2 y2) =
sqrt ((x1 x2)^2 + (y1 y2)^2)
pointX :: Point -> Double
pointX (Pt x _) = x
pointY :: Point -> Double
pointY (Pt _ y) = y

Functional Programming: Spring 2014

Record syntax

Simple product types are common, but having to


explicitly define accessors is a pain:

pointX :: Point -> Double


pointX (Pt x _) = x
pointY :: Point -> Double
pointY (Pt _ y) = y

Functional Programming: Spring 2014

Record syntax

Haskell provides a shortcut where both the datatype


and the accessors can be defined at the same time:

data Point = Pt { pointX :: Double, pointY :: Double }


Prelude>
Point ->
Prelude>
Point ->

:t pointX
Double
:t pointY
Double

Functional Programming: Spring 2014

Record syntax

Can pattern match using record syntax too:

distance :: Point -> Point -> Double


distance (Pt {pointX=x1, pointY=y1})
(Pt {pointX=x2, pointY=y2}) =
sqrt ((x1 x2)^2 + (y1 y2)^2)

In pattern, can put fields of constructor in any order if


name labels are included

Functional Programming: Spring 2014

Record syntax

You might wish that we could do this:

distance :: Point -> Point -> Double


distance p1 p2 =
sqrt ((p1.x p2.x)^2 + (p1.y p2.y)^2)

Alas, this is not legal Haskell syntax


One of the most asked-for syntax extensions
One proposal called "Type Directed Name
Resolution" (see Haskell web pages)

Functional Programming: Spring 2014

Record syntax

Similarly, can't have two different data definitions


which use same field names:

data Point2 = P2 { x ::
data Point3 = P3 { x ::
y ::
z ::

Double, y :: Double }
Double, -- illegal
Double, -- illegal
Double }

Records are thus somewhat clumsy to use in Haskell

Functional Programming: Spring 2014

So far

Weve seen

More generally, many types have both sum and


product components

simple sum types (enumerations)


simple product types (records)

different constructors, each with different number of


arguments

We refer to these as "algebraic datatypes"

Functional Programming: Spring 2014

Natural numbers
Simple example: natural numbers
A natural number is either

zero
the successor of a natural number

Write this in Haskell as:


data Nat =
Zero
| Succ Nat

Functional Programming: Spring 2014

Natural numbers
data Nat =
Zero
| Succ Nat
This defines two constructors: Zero and Succ
Zero is a value
Succ is a "constructor function" with type
Nat -> Nat
Constructors like Succ that have type arguments
can be used as regular functions (though they have
capitalized names)
Functional Programming: Spring 2014

Natural numbers
data Nat =
Zero
| Succ Nat
Note that this type is "recursive"

Defining Nat, but one of the constructors assumes that Nat


has been defined
Haskell has no problem with this

Functional Programming: Spring 2014

Natural numbers
Prelude> :t Zero
Zero :: Nat
Prelude> :t Succ
Succ :: Nat -> Nat
Prelude> :t Succ Zero
Succ Zero :: Nat

Functional Programming: Spring 2014

Natural numbers

Nat definition also defines pattern-matching on


Nats:

addNat :: Nat -> Nat -> Nat


addNat Zero n = n
addNat (Succ m) n = Succ (addNat m n)
mulNat :: Nat -> Nat -> Nat
mulNat Zero _ = Zero
mulNat (Succ m) n = addNat n (mulNat m n)

Functional Programming: Spring 2014

Natural numbers

More functions on Nats

natToInteger :: Nat -> Integer


natToInteger Zero = 0
natToInteger (Succ n) = 1 + natToInteger n

Note: Structure of a Nat:

Succ (Succ (Succ (Succ Zero)))

Want to convert to:

1 + (1 + (1 + (1 + 0)))

What would be a more elegant way to define


natToInteger?
Functional Programming: Spring 2014

Natural numbers
Succ (Succ (Succ (Succ Zero)))

Want to convert to:

1 + (1 + (1 + (1 + 0)))

Seems like we should be able to do something like


foldr here
foldr works only on lists
Let's define foldn to work on Nats
It will specify:

a special value to be used in place of Zero!


a special unary function to be used in place of Succ!
Functional Programming: Spring 2014

Natural numbers
foldn :: (a -> a) -> a -> Nat -> a
foldn _ init Zero = init
foldn f init (Succ n) = f (foldn f init n)

Now we can define:

natToInteger :: Nat -> Integer


natToInteger = foldn (1+) 0

Functional Programming: Spring 2014

Polymorphic datatypes

Algebraic datatypes can also depend on type


variables

like polymorphic functions

Recall the built-in list type

not specified to any particular list element type

Want to be able to do this with user-defined types too


Let's re-create the list type at the user level

Functional Programming: Spring 2014

Polymorphic datatypes
data List a =
Nil
| Cons a (List a)

This defines a family of types called List a


"List of elements of some particular type a"
Isomorphic to normal Haskell list type, which could
be written as:

data [a] =
[]
| a : [a]
Functional Programming: Spring 2014

Polymorphic datatypes

Could define foldr version to work on this List


type:

foldr2 :: (a -> b -> b) -> b -> List a -> b


foldr2 _ init Nil = init
foldr2 f init (Cons h t) = f h (foldr2 f init t)

Moral: built-in list type is not special, except for


syntax

Functional Programming: Spring 2014

Kinds

Note that List is not a type

List Integer is a type


List Float is a type
List Char is a type
but List by itself is not a type!

List is a "type constructor"

like a "function on types"


Give it a type (like Integer) and it will return a type
(List Integer)

Functional Programming: Spring 2014

Kinds
Type constructors do not have "types", they have
"kinds"
A "kind" is a "type of types"
Simple types (non-type constructors) have the kind *
Type constructors have the type (* -> *),
(* -> * -> *) etc. depending on how many type
variables they have
ghci will tell you what the kind of a type constructor
is

Functional Programming: Spring 2014

Kinds
Prelude> :info List
data List a = Nil | Cons a (List a)
Prelude> :kind List
List :: * -> *
Prelude> :kind List Integer
List Integer :: *
Prelude> :kind Integer
Integer :: *

Can abbreviate :kind as just :k

Functional Programming: Spring 2014

Maybe
Another useful polymorphic type constructor is
Maybe
Used to represent values that "may or may not exist"

data Maybe a =
Nothing
| Just a

Mostly useful as a function return argument


Functions of type a -> Maybe b represent
computations that may fail
Maybe is also a monad (as we'll see later)

Functional Programming: Spring 2014

Maybe

Let's ask ghci about Maybe

Prelude> :i Maybe
data Maybe = Nothing | Just a
Prelude> :t Nothing
Nothing :: Maybe a
Prelude> :k Maybe
Maybe :: * -> *

Functional Programming: Spring 2014

Either
Polymorphic datatypes can depend on more than
one type variable
Simplest example: Either type constructor
data Either a b =
Left a
| Right b
Either allows us to define a type which can be
either of two arbitrary types
For instance, Either Int String is either an Int
or a String

Functional Programming: Spring 2014

Either
Again, Either is often used as a return type from a
function
Functions with the type a -> Either String b
can represent functions that either

succeed with a value of type b


fail with an error message (String value)

This also constitutes a monad (as we'll see later)

Prelude> :k Either
Either :: * -> * -> *

Functional Programming: Spring 2014

Trees
Polymorphic datatypes very often used to represent
generic data structures
For instance, binary trees of some type a

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

For now, we won't worry about balancing or ordering


(save for later)

Functional Programming: Spring 2014

Trees

Function to collect Tree values into a list in order:

treeToList :: Tree a -> [a]


treeToList Leaf = []
treeToList (Node x left right) =
treeToList left ++ [x] ++ treeToList right

Again, note pattern matching on Tree constructors

Functional Programming: Spring 2014

Type aliases
data declarations are the normal way to create new
Haskell datatypes
Sometimes, we have an existing type with an
unpleasant name (too long, not descriptive enough)
but don't want/need to define a completely new type
We can define a type alias using the type keyword:
type String = [Char]
This defines String as another name for [Char]

Functional Programming: Spring 2014

Type aliases

Type aliases mean that ghci can't always choose


the name for a type you might prefer:

Prelude> :t "foobar"
"foobar" :: [Char]
-- not String

Type aliases also may mean that error messages


involving types may refer to the aliased name or the
unaliased name
Therefore, type aliases are mainly a convenience for
the code writer

Functional Programming: Spring 2014

newtype
Type aliases only provide a new name for an old
type, not a new type
Sometimes, we want to define a new type for a
previously-existing type in such a way that the new
type is identifiably distinct from values of the old type
but the contents are the same
Can do this with a data declaration e.g.:
data Label = Lbl String
Now Haskell considers Label and String to be
distinct types

Functional Programming: Spring 2014

newtype
data Label = Lbl String
Difference between a value of type Label and a
value of type String:

Label value is "wrapped" in the constructor Lbl, String


value is not
This "wrapping" means that Label values take up more
space than Strings, and must be unwrapped to get the
contents (space and time costs)
What if we wanted to keep the Label and String types
distinct, but not pay this cost?

Functional Programming: Spring 2014

newtype
Define Label as a newtype:
newtype Label = Lbl String
Differences between data and newtype:

newtype only allowed for datatypes with one constructor


which has exactly one type argument
newtype defined datatypes are just as efficient as the type
they wrap (no wrapping/unwrapping penalty), but are
distinct to the type checker
(and a few other subtle issues you're unlikely to run into for
a long time)
Functional Programming: Spring 2014

Next time

Type classes!

Functional Programming: Spring 2014

CS 115
Functional Programming
Lecture 7: April 18, 2014

Type Classes, part 1

Functional Programming: Spring 2014

Today
Type classes
Motivation
Examples: Eq, Ord, Num, Show
How type classes are implemented
Type classes and algebraic datatypes

Functional Programming: Spring 2014

Motivation: operators
Many operations referred to by a single name
actually behave differently when used on different
types
Example: + (addition)
Operation of adding two integers is completely
different from operation of adding two floating-point
numbers

Other kinds of numbers have still other definitions


Yet we use the same symbol (+) for all of these!

Functional Programming: Spring 2014

Motivation: operators

Use of common symbols for different operations


comes from mathematics

simplifies notation
can use context (type information) to disambiguate actual
intended operations

Most computer languages "overload" such operators


based on the types of the operands
However, such overloading is usually hard-wired
(non-extensible to new types)

Functional Programming: Spring 2014

Motivation: operators
Some languages (e.g. C++) allow user-defined
operator overloading
Still major limitations:

e.g. cannot define new operators (fixed set)


operators have no semantic content, so can lead to hard-tounderstand code

Other languages (e.g. Java) forbid operator


overloading

weakens expressive power of language

Functional Programming: Spring 2014

Motivation: functions
Operators are not the only language entities that can
conceptually be defined for multiple types
Often have a notion of a function which should be
specialized based on a particular type

e.g. "convert a value of this type to a string"


this is a generic function for this functionality

Some languages deal with this through objectoriented features

classes, instances, interfaces

Functional Programming: Spring 2014

Type classes
Haskell uses type classes to represent generic
operations both at the operator and function level
Type classes provide a very clean solution to the
problem of operator overloading
Also provide a very convenient way to define generic
functions
IMO: One of the uniquely wonderful features of
Haskell, responsible for much of its power

also: many extensions!


Functional Programming: Spring 2014

Type classes
Type classes referred to sometimes as "ad-hoc
polymorphism"
In contrast to previous kind of polymorphism, which
is called "parametric polymorphism" (due to
generalizing on type parameters)
"Ad-hoc" means that it is essentially arbitrary which
types instantiate which type classes
Also open: can add new type class instances at any
time after definition

Functional Programming: Spring 2014

Equality
First example: equality
Many data values have some well-defined notion of
how to compare two such values to see if they are
"equal"
Some data values do not (notably functions)
We use

the == operator to test two values for equality


the /= operator to test two values for inequality

Functional Programming: Spring 2014

Equality
Consider two types: Int and Float
Both have well-defined notions of equality
comparison
Comparing two Ints for equality a completely
different operation than comparing two Floats
Worst case: could define intEq and floatEq
functions with these type signatures:

intEq :: Int -> Int -> Bool


floatEq :: Float -> Float -> Bool
Functional Programming: Spring 2014

Equality

We can extend this to new types:

charEq :: Char -> Char -> Bool


stringEq :: String -> String -> Bool

Also, would want to leave open the possibility of


defining new equality operations later for userdefined types

e.g. treeEq :: Tree -> Tree -> Bool for some


Tree data type

Functional Programming: Spring 2014

Equality

Shape of type signature of all these functions

xEq :: x -> x -> Bool

It would be nice if there was a way to make the ==


operator work on all equality functions of this kind

including user-defined ones like treeEq

Functional Programming: Spring 2014

Eq
The Haskell Prelude defines the Eq type class for
this very purpose
Definition:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool

Functional Programming: Spring 2014

Eq
Interpretation:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
Eq is a type class with one type parameter a
It defines the meanings of two operators: == and /=
Each of them takes two arguments of type a and
returns a Bool

Functional Programming: Spring 2014

class
The class definition defines the functions (AKA
methods) of the type class along with their type
signatures
Type signatures in type class definitions always
depend on type parameter a (or it would be useless)
No other semantic information included in type class

e.g. that two values can either be == or /=, but not both and
not neither is not part of the definition
Haskell isn't that powerful!

Functional Programming: Spring 2014

class
The class terminology is utterly unrelated to objectoriented programming (OOP) terminology
Terms like class, instance, method are used but
mean completely different things than in OOP
languages!
Closest match to OOP: type classes are like
"compile-time interfaces"

Functional Programming: Spring 2014

instance
Given a class, must be able to create instances of
the class
Assume have functions intEq, floatEq for Int,
Float equality comparisons
Can define instances of Eq for Int and Float

Functional Programming: Spring 2014

instance
Instances are defined as follows:
instance Eq Int where
(==) = intEq
x /= y = not (x == y)
-- or: (/=) = (not .) . (==)

instance Eq Float where


(==) = floatEq
x /= y = not (x == y)
Functional Programming: Spring 2014

instance
If you defined a Tree data type and treeEq:
instance Eq Tree where
(==) = treeEq
x /= y = not (x == y)

Functional Programming: Spring 2014

Default definitions
Note redundancy in definition of /= operator:
instance Eq XXX where
(==) = xxxEq
x /= y = not (x == y)
Nearly all types will define /= this way
"Boilerplate" code (code with standard structure,
repeated frequently) is anathema to the Haskell
programmer
Therefore, Haskell provides a shortcut

Functional Programming: Spring 2014

Default definitions
Can define either == or /= in terms of the other
Class definition becomes
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)

Functional Programming: Spring 2014

Default definitions
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
Now only need to define either == or /= for any Eq
instance

other is supplied automatically from default definitions

Functional Programming: Spring 2014

Type classes and functions


Note type of (==) operator in ghci:
Prelude> :t (==)
Eq a => a -> a -> Bool
This type signature says "for any type a such that a is an
instance of Eq, the type of == is a -> a -> Bool"
The => is a context arrow
LHS of => is the (type) context that the RHS must have
Can write our own functions with type signatures like this

Functional Programming: Spring 2014

Type classes and functions

Example function

allEqual
allEqual
allEqual
allEqual
allEqual

:: (Eq a) => [a] -> Bool


[] = True
[_] = True
(x:y:xs) | x == y = allEqual (y:xs)
_ = False

Now allEqual can be applied to a list of any type a,


as long as that type is an instance of Eq
(Eq a) => specifies the context for the types in the
type signature

Functional Programming: Spring 2014

Ord
Another very useful type class is Ord
Represents types whose values can be compared
with each other
Definition:

class (Eq a) => Ord a where


compare :: a -> a -> Ordering
(<), (<=), (>), (>=) :: a -> a -> Bool
max, min :: a -> a -> a

plus various default definitions


Minimal instance definition: compare or (<=)

Functional Programming: Spring 2014

Ord

Ordering is the following data type:

data Ordering = LT | EQ | GT

Note context in class definition:

class (Eq a) => Ord a where ...

This states that for a type to be an instance of Ord, it


must first be an instance of Eq (makes sense)
Note that we can write multiple method signatures on
one line if the type signature is the same

(<), (<=), (>), (>=) :: a -> a -> Bool

Functional Programming: Spring 2014

Ord

Recall quicksort definition:

quicksort :: [Integer] -> [Integer]


quicksort [] = []
quicksort (x:xs) =
quicksort lt ++ [x] ++ quicksort ge
where
lt = [y | y <- xs, y < x]
ge = [y | y <- xs, y >= x]

Nothing here is particularly specific to Integers


How do we generalize this?

Functional Programming: Spring 2014

Ord

Use Ord constraint:

quicksort :: Ord a => [a] -> [a]


quicksort [] = []
quicksort (x:xs) =
quicksort lt ++ [x] ++ quicksort ge
where
lt = [y | y <- xs, y < x]
ge = [y | y <- xs, y >= x]

Now it will work on any orderable type!

Functional Programming: Spring 2014

Num
Haskell has a hierarchy of numeric type classes
Most basic one is called Num (for "numeric type")
Definition:

class Num a where


(+), (-), (*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a

Functional Programming: Spring 2014

Num
Num instances represent what we expect all numbers
to be able to do
In older versions of GHC, Num had these class
constraints:

class (Eq a, Show a) => Num a where ...

These constraints have been removed!

Show was always a bogus constraint anyway, Eq less so

Num instances also do not need to be instances of


Ord

why?
Functional Programming: Spring 2014

Num
Methods:
+ - * negate abs have the usual meanings
signum represents "sign" so that
abs x * signum x = x
fromInteger converts an Integer into a value of
this numeric type a

Functional Programming: Spring 2014

Integer literals

Type classes even evident in the types of integer


literals:

Prelude> :type 42
42 :: Num a => a

The number 42 has no specific type!


It is of type a, where a is any Num instance
Num instances include Int, Integer, Float,
Double
Therefore 42 is a valid literal for any of those types

Prelude> :t (42 :: Float)


(42 :: Float) :: Float
Functional Programming: Spring 2014

Num example

Simple function using Num:

sumOfSquares :: Num a => a -> a -> a


sumOfSquares x y = x * x + y * y
Prelude> sumOfSquares 3 4
25
Prelude> sumOfSquares 1.2 3.4
12.999999999999998

sumOfSquares works generically for any Num


instance
Functional Programming: Spring 2014

Implementation of type classes


Type classes are implemented as a record of
methods that is passed as an extra argument to
functions using type classes
Compiler supplies the extra arguments
Example: Num instances represented as a record
something like this:

data NumRecord a =
NR { addOp :: a -> a -> a, subOp :: a -> a -> a,
mulOp :: a -> a -> a,
negateFn :: a -> a, absFn :: a -> a,
signumFn :: a -> a,
fromIntegerFn :: Integer -> a }

Functional Programming: Spring 2014

Implementation of type classes

For a particular Num instance (e.g. Int), populate


record with methods:

intNumRecord :: NumRecord Int


intNumRecord = NR intAddOp intSubOp intMulOp intNegateFn
intAbsFn intSignumFn intFromIntegerFn

Change definitions and function calls using Num to


have extra arguments:

sumOfSquares :: NumRecord a -> a -> a -> a


sumOfSquares n x y = addOp n (mulOp n x x) (mulOp n y y)

Note: We use addOp instead of (+) etc. because


operators can only have two arguments
Functional Programming: Spring 2014

Implementation of type classes

Internally, definition of e.g. addOp would be


something like this:

addOp :: NumRecord a -> a -> a -> a


addOp (NR add _ _ _ _ _ _) x y = add x y

Compiler does all of these transformations for you


Type classes are thus nothing more than normal
functional programming with some fairly heavy
syntactic sugar

Functional Programming: Spring 2014

Constrained datatypes
In older versions of GHC, type class constraints
could occur in datatype definitions as well
Consider an ordered binary tree with data in
branches
Left subbranch contains only data "less than" data
stored in a node
Right subbranch contains only data "greater than"
data stored in a node
Let's write the datatype

Functional Programming: Spring 2014

Constrained datatypes
data Ord a => Tree a =
-- not legal anymore!
Leaf
| Node a (Tree a) (Tree a)

Let's write a function on this datatype:

inTree
inTree
inTree
case
LT
GT
EQ

:: Ord a => a -> Tree a -> Bool


_ Leaf = False
x (Node y left right) =
compare x y of
-> inTree x left
-> inTree x right
-> True

Functional Programming: Spring 2014

Constrained datatypes
Problem: Having a constraint on a datatype doesn't
remove the requirement for adding it to functions on
that datatype:
Our previous definition:

data Ord a => Tree a = ...

Note the function:

inTree :: Ord a => a -> Tree a -> Bool

still needs to have the Ord constraint!


Therefore, it's generally considered a bad idea to add
constraints directly to datatypes (useless)

Now requires the DatatypeContexts compiler option


Functional Programming: Spring 2014

Show
Another very useful type class is Show
Represents notion of "something that can be
converted to a String"
Definition:
class Show a where
show :: a -> String
[A couple of other methods as well, not relevant for
now]

Functional Programming: Spring 2014

Show
To view a datatype in ghci, need to define a Show
instance
Example: Test.hs

data Color = Red | Green | Blue | Yellow

In ghci:

Prelude> :l ./Test.hs
Prelude> :t Red
Red :: Color

So far, so good

Functional Programming: Spring 2014

Show
Prelude> Red
<interactive>:1:1:
No instance for (Show Color)
arising from a use of `print'
Possible fix: add an instance declaration for (Show Color)
In a stmt of an interactive GHCi command: print it

What happened?
ghci is a "read-eval-print" loop (REPL)
It reads an expression, evaluates it, and prints the
result
It can only print the result if the result can be printed!

Functional Programming: Spring 2014

Show
Prelude> Red
<interactive>:1:1:
No instance for (Show Color)
arising from a use of `print'
Possible fix: add an instance declaration for (Show Color)
In a stmt of an interactive GHCi command: print it

If no Show instance has been defined for the Color


datatype, ghci can't do the printing error
message
Error message even suggests what you need to do!
So let's do it

Functional Programming: Spring 2014

Show

In Test.hs:

data Color = Red | Green | Blue | Yellow


instance Show Color where
show Red
= "Red"
show Green = "Green"
show Blue
= "Blue"
show Yellow = "Yellow"

Functional Programming: Spring 2014

Show

In ghci:

Prelude> :l ./Test.hs
Prelude> Red
Red

Woo hoo!
Problem: This is boring "boilerplate" code
Haskell programmers hate boilerplate code!
We'll see a way to get around this next lecture

Functional Programming: Spring 2014

Next time
More type classes
Deriving type classes automatically
Constructor classes and Functor
Multi-parameter type classes
Tour of Haskell type classes

Functional Programming: Spring 2014

You might also like