Lec1 7
Lec1 7
Functional Programming
Lecture 1: April 2, 2014
Today
Course overview and policies
Motivation (course philosophy)
Introduction to Haskell
Course overview
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
Assignments
Like CS 1:
Web site
On moodle.caltech.edu
Password is iheartmonads
Textbooks
Textbooks
Textbooks
Textbooks
Textbooks
Course outline
First half:
Course outline
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
e.g. aliasing
references to objects behave differently than copies
of objects
"off-by-one" errors in loops
Advantages of mutation
e.g. simulations
Programming paradigms
"Functional style"
Other FP features
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
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...
Haskell
A non-strict, purely functional language
Non-strict ("lazy"): expressions are never
computed unless their values are needed
Purely functional:
Haskell
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
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.
Why Haskell?
Personal observations
Beginning of details
About Haskell
Haskell is a compiled language
Can also be run using an interpreter
Haskell as a calculator
We'll work mostly with ghci at first
Start up ghci
% ghci
[... some descriptive text ...]
Prelude>
Prelude> 2 + 2<return>
4
Woo hoo!
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
Comments
-- this is a comment
{- this is a
multiline
comment -}
File/ghci interaction
ghci is good for interactive experimentation/
testing of code
Cannot enter arbitrary code into ghci (some
limitations)
Best approach:
File/ghci interaction
File/ghci interaction
Prelude> :l Foo.hs
*Foo>
Function definitions
Function definitions
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
Function definitions
double x = 2 * x
Types
Consider:
Types
Consider:
Types
Pattern matching
Pattern matching
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n 1)
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
if expression
if expression
factorial :: Integer -> Integer
factorial n = if n == 0
then 1
else n * factorial (n 1)
Next time
More Haskell basics
Evaluation in Haskell
CS 101c
Functional Programming
Lecture 2: April 7, 2014
Evaluation
Today
More Haskell basics
Introduction to Haskell's evaluation model
Int
Integer
Float
Double
Char
Bool
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
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
Examples:
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]
Lists
Empty list written as []
Lists are constructed using the : (cons) operator
1 : (2 : (3 : (4 : (5 : []))))
== [1, 2, 3, 4, 5]
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!
Tuples
A tuple is a sequence of values inside
parentheses, separated by commas
Tuples can contain values of different types:
(,) 1 2 (1, 2)
(,,) 1 "foo" 3.14 (1, "foo", 3.14)
Identifier syntax
Operator syntax
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
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)
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
Operator precedence
Prelude> :info +
infixl 6 +
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)
Operator precedence
When defining a new operator, default
precedence is 9 (highest); default associativity is
left
Can specify precedence/associativity explicitly
Way 1:
Way 2:
Way 2:
Pitfall:
Operator sections
Prelude> (*2) 10
20
Prelude> (9/) 3
3
Local definitions
let expressions
where declarations
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
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
let expression
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
let expression
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!
where declaration
-- 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
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
Example 1
double :: Integer -> Integer
double x = x + x
Evaluate: double (3 * 4)
Multiple possibilities exist!
In general:
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
Example 1
Evaluate: double (3 * 4)
Attempt 2:
Example 1
Evaluate: double (3 * 4)
Attempt 3:
Strict evaluation:
Lazy evaluation:
Example 2
infinity :: Integer
infinity = infinity + 1
Try to evaluate:
infinity
infinity + 1
(infinity + 1) + 1
((infinity + 1) + 1) + 1
Example 3
three :: Integer -> Integer
three n = 3
three infinity
3
Example 3
Recall that non-terminating expressions like
infinity are denoted by _|_ (bottom)
Definition of lazy/strict functions:
Example 4
Example 4
factorial 3
Example 4
Continuing
Example 4
Continuing
Example 4
Example 5
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
Next time
More Haskell basics
Lists
Polymorphic types
Function composition
Point-free and point-wise style
CS 115
Functional Programming
Lecture 3: April 9, 2014
Lists
Today
More Haskell basics
Polymorphic types
Lists
Functions on lists
Equality operators
Prelude> 1 == 2
False
Prelude> 1 /= 2
True
Equality operators
Prelude> 1 /= 'c'
[type error]
Prelude> 1 /= 2
True
Prelude> 'a' == 'a'
True
Functional Programming: Spring 2014
Equality operators
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool
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!
Boolean operators/functions
Negative numbers
Prelude> -10
-10
Prelude> 3 + -10
[precedence parsing error]
Prelude> 3 + (-10)
-7
Pattern guards
Pattern matching matches on structural
features of function arguments
Sometimes need to test for non-structural
features (arbitrary predicates)
Pattern guards
abs x | x < 0 = -x
Basic structure:
Multiple guards
abs x | x < 0 = -x
| x > 0 = x
| x == 0 = 0
abs x | x < 0 = -x
abs x | x > 0 = x
abs x | x == 0 = 0
Pattern wildcards
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
Pitfall
abs x | x < 0 = -x
| x > 0 = x
| x == 0 = 0
Pitfall
Fix:
abs x | x < 0 = -x
| x > 0 = x
| otherwise = 0
Prelude> otherwise
True
Try:
Prelude> abs
10
Prelude> abs
[nasty error
Prelude> abs
10
-10
message!]
(-10)
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]
error
factorial 0 = 1
factorial n = n * factorial (n 1)
error
factorial 0 = 1
factorial n | n < 0 = error "bad input"
factorial n = n * factorial (n 1)
Lists
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:
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
Data.List
Data.List
import Data.List
Data.List
Lists
construction
deconstruction (pattern matching)
the empty list ([])
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
3 : 2 : 1 : [] is a list of Ints
Syntactic sugar: write as [3, 2, 1]
List construction
Precedence 5, right-associative
We'll talk about data declaration next time
List construction
Prelude> 1 : [2, 3, 4, 5]
[1, 2, 3, 4, 5]
List construction
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)
List deconstruction
Answer: head of list, and tail of list
Example function: length of a list
List deconstruction
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
List deconstruction
foo
foo
foo
foo
foo
++ 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]
++ operator
++ operator
Definition of ++ operator:
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
[] ++ ys = ys
(x:xs) ++ ys = [x] ++ (xs ++ ys)
Functional Programming: Spring 2014
concat
The concat function concatenates a list of lists
One definition:
reverse
The reverse function reverses a list
One definition:
reverse
Alternate definition:
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
The .. syntax
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
Infinite lists
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
zipWith
zipWith is like zip, except that it applies a twoargument function to the two-tuples
and and or
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
CS 115
Functional Programming
Lecture 4: April 11, 2014
Today
Higher-order functions on lists
Anonymous functions
Simple code transformations
Point-free style
More on lazy evaluation
Higher-order functions
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:
List functions
Functions on lists include many very useful
higher-order functions
Two simple examples: map and filter
map
map
What is map's type signature?
map :: (a -> b) -> [a] -> [b]
map takes
and returns
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]
Anonymous functions
Often we want to create a function for a
single use
For example: double function on previous
slide
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
Anonymous functions
Example:
Syntax:
Puzzle
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
\x y -> x + y
\x -> \y -> x + y
(due to currying)
Puzzle
This is equivalent to
Definition of map
map :: (a -> b) -> [a] -> [b]
map f [] = [] -- or use _ for f
map f (x:xs) = f x : map f xs
Interesting definition:
integers :: [Integer]
integers = 1 : map (1+) integers
Let's evaluate:
take 3 integers
Functional Programming: Spring 2014
N.B. This isn't exactly the same as the built-in take function
Evaluate:
take 3 integers
Evaluate:
take 3 integers
Reduce:
Continue:
Answer: [1, 2, 3]
Note that
filter
filter
Definition of filter:
filter
Examples of filter:
twiceNonzeros
twiceNonzeros
twiceNonzeros
twiceNonzeros
New definition:
Much nicer!
The $ operator
twiceNonzeros :: [Int] -> [Int]
twiceNonZeros xs = map (2*)
(filter (\x -> x /= 0) xs)
twiceNonZeros xs =
map (2*) $ filter (\x -> x /= 0) xs
The $ operator
Prelude> :info $
($) :: (a -> b) -> a -> b
infixr 0 $
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 $
Function composition
twiceNonZeros xs =
map (2*) $ filter (\x -> x /= 0) xs
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!
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
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
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
\x -> f x
f
twiceNonZeros xs =
(map (2*) . filter (/= 0)) xs
twiceNonZeros xs =
map (2*) $ filter (/= 0) xs
Into:
twiceNonZeros =
map (2*) $ filter (/= 0)
Why not?
Reason:
twiceNonZeros xs =
map (2*) $ filter (/= 0) xs
twiceNonZeros xs =
(map (2*) $ filter (/= 0)) xs
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!
Point-free style
Point-free style
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)
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?
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])
Efficiency
map (2*) (1 : filter (/= 0) [0, 2, 0, 3, 0])
2 : (map (2*) (filter (/= 0) [0, 2, 0, 3, 0]))
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
Efficiency
Continuing
2
2
2
2
2
2
2
2
2
2
:
:
:
:
:
:
:
:
:
:
[2, 4, 6]
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!
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)
Next time
More higher-order list functions: foldr
and friends
List comprehensions
CS 115
Functional Programming
Lecture 5: April 14, 2014
Today
List comprehensions
@-patterns
@-patterns
insert
insert
insert
insert
@-patterns
insert
insert
insert
insert
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
case expression
Familiar example:
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
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
Problem
Let's write a function to add up all the numbers in a
list of Integers
Writing a recursive definition is straightforward:
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
Generalizing sum
First try:
sum [] = 0
sum (n:ns) = n + sum ns
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:
(1 : (2 : (3 : (4 : (5 : [])))))
a function of two arguments
an initial value
foldr
What does foldr actually do?
foldr takes
an operator (op)
an initial value (init)
a list
foldr
foldr (+) 0 [1, 2, 3, 4, 5]
foldr (+) 0 (1 : (2 : (3 : (4 : (5 : [])))))
1 +
1 +
1 +
1 +
1 +
15
(2
(2
(2
(2
14
+
+
+
+
(3 + (4 + (5 + 0)))))
(3 + (4 + 5))))
(3 + 9)))
12)
foldr
foldr is a very flexible function
Many other Haskell functions can be defined in
terms of foldr
Let's see some examples
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) = ?
concat
concat :: [[a]] -> [a]
concat [] = []
concat (xs:xss) = xs ++ (concat xss)
In terms of foldr:
concat xss = foldr (++) [] xss
or just:
concat = foldr (++) []
concat
Why this works:
concat [lst1, lst2, ...]
= lst1 ++ lst2 ++ ... ++ []
++
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]
++
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
++
(++) 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
++
foldr is powerful!
Warning: excessive use of point-free style can lead
to impossible-to-understand code!
Definition:
Awesome! J
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
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
Definition:
foldl
sum = foldl (+) 0
This is a valid definition of sum
foldl is tail-recursive ("iterative") so might
consume less space than foldr version
List comprehensions
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
List comprehensions
Syntax:
[<expression> | <generators>, <filters>]
Generators have the form
x <- [1..1000]
(x is taken elementwise from the list [1..1000])
List comprehensions
Two generators:
Filters
Patterns in generators
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]
Examples
Next time
Defining new data types (algebraic data types)
Coming soon: Type classes
CS 115
Functional Programming
Lecture 6: April 16, 2014
Algebraic Datatypes
Today
Defining new datatypes in Haskell
Enumeration-style datatypes
Datatypes with arguments
Constructor functions
Record syntax
Recursive and polymorphic datatypes
Type synonyms
newtype
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
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
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
ghci
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
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)
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 _|_ = _|_
Example: || function
Logical or function uses the || operator
One definition:
Example: || function
(||) :: Bool -> Bool -> Bool
True || True = True
True || False = True
False || True = True
False || False = False
Example: || function
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
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
data
data
data
data
Color
Day
Beatle
Major
=
=
=
=
etc.
All such definitions also define pattern-matching on
the corresponding datatypes
data Color
opposite
opposite
opposite
opposite
opposite
:: Color
Red
=
Green =
Blue
=
Yellow =
-> Color
Green
Red
Yellow
Blue
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:
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
Product types
Examples:
-- x and y coords
-- first and last names
-- field, number
Product types
Record syntax
Record syntax
:t pointX
Double
:t pointY
Double
Record syntax
Record syntax
Record syntax
data Point2 = P2 { x ::
data Point3 = P3 { x ::
y ::
z ::
Double, y :: Double }
Double, -- illegal
Double, -- illegal
Double }
So far
Weve seen
Natural numbers
Simple example: natural numbers
A natural number is either
zero
the successor of a natural number
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"
Natural numbers
Prelude> :t Zero
Zero :: Nat
Prelude> :t Succ
Succ :: Nat -> Nat
Prelude> :t Succ Zero
Succ Zero :: Nat
Natural numbers
Natural numbers
1 + (1 + (1 + (1 + 0)))
Natural numbers
Succ (Succ (Succ (Succ Zero)))
1 + (1 + (1 + (1 + 0)))
Natural numbers
foldn :: (a -> a) -> a -> Nat -> a
foldn _ init Zero = init
foldn f init (Succ n) = f (foldn f init n)
Polymorphic datatypes
Polymorphic datatypes
data List a =
Nil
| Cons a (List a)
data [a] =
[]
| a : [a]
Functional Programming: Spring 2014
Polymorphic datatypes
Kinds
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
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 :: *
Maybe
Another useful polymorphic type constructor is
Maybe
Used to represent values that "may or may not exist"
data Maybe a =
Nothing
| Just a
Maybe
Prelude> :i Maybe
data Maybe = Nothing | Just a
Prelude> :t Nothing
Nothing :: Maybe a
Prelude> :k Maybe
Maybe :: * -> *
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
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
Prelude> :k Either
Either :: * -> * -> *
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)
Trees
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]
Type aliases
Prelude> :t "foobar"
"foobar" :: [Char]
-- not String
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
newtype
data Label = Lbl String
Difference between a value of type Label and a
value of type String:
newtype
Define Label as a newtype:
newtype Label = Lbl String
Differences between data and newtype:
Next time
Type classes!
CS 115
Functional Programming
Lecture 7: April 18, 2014
Today
Type classes
Motivation
Examples: Eq, Ord, Num, Show
How type classes are implemented
Type classes and algebraic datatypes
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
Motivation: operators
simplifies notation
can use context (type information) to disambiguate actual
intended operations
Motivation: operators
Some languages (e.g. C++) allow user-defined
operator overloading
Still major limitations:
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
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
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
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
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:
Equality
Equality
Eq
The Haskell Prelude defines the Eq type class for
this very purpose
Definition:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
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
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!
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"
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
instance
Instances are defined as follows:
instance Eq Int where
(==) = intEq
x /= y = not (x == y)
-- or: (/=) = (not .) . (==)
instance
If you defined a Tree data type and treeEq:
instance Eq Tree where
(==) = treeEq
x /= y = not (x == y)
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
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)
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
Example function
allEqual
allEqual
allEqual
allEqual
allEqual
Ord
Another very useful type class is Ord
Represents types whose values can be compared
with each other
Definition:
Ord
data Ordering = LT | EQ | GT
Ord
Ord
Num
Haskell has a hierarchy of numeric type classes
Most basic one is called Num (for "numeric type")
Definition:
Num
Num instances represent what we expect all numbers
to be able to do
In older versions of GHC, Num had these class
constraints:
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
Integer literals
Prelude> :type 42
42 :: Num a => a
Num example
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 }
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
Constrained datatypes
data Ord a => Tree a =
-- not legal anymore!
Leaf
| Node a (Tree a) (Tree a)
inTree
inTree
inTree
case
LT
GT
EQ
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:
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]
Show
To view a datatype in ghci, need to define a Show
instance
Example: Test.hs
In ghci:
Prelude> :l ./Test.hs
Prelude> :t Red
Red :: Color
So far, so good
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!
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
Show
In Test.hs:
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
Next time
More type classes
Deriving type classes automatically
Constructor classes and Functor
Multi-parameter type classes
Tour of Haskell type classes