Functional Languages: CWRU/EECS345/Beer
Functional Languages: CWRU/EECS345/Beer
• Defining features
➪ Programs as collections of functions
➪ Function call (applicative)
➪ No variables (but names are allowed)
➪ No assignment
➪ No iteration (recursion)
• Grounded in recursive function theory (Church’s λ calculus)
• First introduced in Lisp (developed by John McCarthy in the
late 1950s)
CWRU/EECS345/Beer
Scheme
• Scheme is a minimalist Lisp
• Developed by Steele and Sussman (1975-1978)
• ANSI standard since 1990
• Popular teaching language since Abelson and Sussman’s
“Structure and Interpretation of Computer Programs” (1985)
CWRU/EECS345/Beer
Functional Example: Scheme
fact(n) ≡ n × (n-1) × ... × 1
⇓
fact(n) = n × fact(n-1)
fact(0) = 1
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(if (= n 0)
1
(* n (fact (- n 1)))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(* 10 (fact 9))
(if (= n 0)
1
(* n (fact (- n 1)))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(* 10 (fact 9))
(if (= n 0)
(* 10 (* 9 (fact 8)))
1
(* n (fact (- n 1)))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(* 10 (fact 9))
(if (= n 0)
(* 10 (* 9 (fact 8)))
1
(* 10 (* 9 (* 8 (fact 7))))
(* 10 (* 9 (* 8 (* 7 (fact 6)))))
(* n (fact (- n 1)))))
(* 10 (* 9 (* 8 (* 7 (* 6 (fact 5))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (fact 4)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (fact 3)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (fact 2)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (fact 1))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(* 10 (fact 9))
(if (= n 0)
(* 10 (* 9 (fact 8)))
1
(* 10 (* 9 (* 8 (fact 7))))
(* 10 (* 9 (* 8 (* 7 (fact 6)))))
(* n (fact (- n 1)))))
(* 10 (* 9 (* 8 (* 7 (* 6 (fact 5))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (fact 4)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (fact 3)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (fact 2)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (fact 1))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (* 1 1))))))))))
CWRU/EECS345/Beer
Recursive Factorial in Scheme
(define (fact n)
(fact 10)
(* 10 (fact 9))
(if (= n 0)
(* 10 (* 9 (fact 8)))
1
(* 10 (* 9 (* 8 (fact 7))))
(* 10 (* 9 (* 8 (* 7 (fact 6)))))
(* n (fact (- n 1)))))
(* 10 (* 9 (* 8 (* 7 (* 6 (fact 5))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (fact 4)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (fact 3)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (fact 2)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (fact 1))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 (* 1 1))))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 1)))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 2))))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 6)))))))
(* 10 (* 9 (* 8 (* 7 (* 6 (* 5 24))))))
(* 10 (* 9 (* 8 (* 7 (* 6 120)))))
(* 10 (* 9 (* 8 (* 7 720))))
(* 10 (* 9 (* 8 5040)))
(* 10 (* 9 40320))
(* 10 362880) Substitution Semantics
3628800
(β reduction)
CWRU/EECS345/Beer
Imperative vs. Functional
int fact(int n) (define (fact n)
{
(if (= n 0)
int result;
1
result = 1;
(* n (fact (- n 1)))))
while (n > 0) {
result = result * n;
n = n - 1;
}
return result;
int fact(int n)
}
{
if (n == 0)
return 1;
else
return n * fact(n-1);
}
CWRU/EECS345/Beer
CWRU/EECS345/Beer
Scheme: Values
• Numbers: 5, 3.14, 1/3, 2.1-3i
• Symbols: pi, symbol-table
• Defining global constants
➪ (define pi 3.14159)
➪ pi ⇒ 3.14159
➪ Primitive functions are just symbols that evaluate to
executable code: + ⇒ #<primitive:+>
• Expressions: (+ 1 (* 2 (- pi 1))) ⇒ 5.28318
• Boolean: #f (false) and #t (true)
• Strings: “Hello, world!”
• Lists
➪ A list of symbols and numbers: (a 1 b 2)
➪ The empty list: ()
➪ Lists can be nested: (a (b () c) (1 (2)))
CWRU/EECS345/Beer
Scheme: Evaluation
• The evaluation rule
➪ Constants are self-evaluating: 2 2
➪ Symbols are evaluated by looking up their values:
pi 3.14159
➪ Lists are evaluated by
Evaluate each element
Apply the first element to the remaining elements
(* 2 pi) → (#<primitive:*> 2 3.14159) 6.28318
• if is one of a small number of “special” functions (Why?)
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
CWRU/EECS345/Beer
Scheme: Quoting
• A problem: How to stop evaluation?
➪ If pi ⇒ 3.14159, how to manipulate the symbol pi?
➪ If (+ 1 2) ⇒ 3, how to manipulate the list (+ 1 2)?
• A solution: Quoting
• Quoting in English
➪ The word metaphor is very strange
➪ The word “metaphor” is very strange
• Examples
➪ (quote pi) or ‘pi ⇒ pi
➪ ‘(+ 1 2) ⇒ (+ 1 2)
➪ (list (+ 1 2) 3) ⇒ (3 3)
➪ (list ‘(+ 1 2) 3) ⇒ ((+ 1 2) 3)
➪ ‘’pi ⇒ ’pi
(quote (quote pi)) ⇒ (quote pi)
CWRU/EECS345/Beer
Scheme: Predicates
• Is it the empty list: null?
➪ (null? ‘()) ⇒ #t
➪ (null? 5) ⇒ #f
• Is it a number: number?
➪ (number? 5) ⇒ #t
➪ (number? ‘pi) ⇒ #f but (number? pi) ⇒ #t
• Is it a list: list?
➪ (list? ‘(+ 1 2)) ⇒ #t but (list? (+ 1 2)) ⇒ #f
➪ (list? ‘()) ⇒ #t
• Others: boolean?, string?, symbol?, etc.
CWRU/EECS345/Beer
Scheme: Equality
• Two objects are equivalent if they have the same structure and
contents
• Examples
➪ (equal? 1 5) ⇒ #f
➪ (equal? ‘(1 2) ‘(1 2)) ⇒ #t
➪ (equal? 5 ‘(5)) ⇒ #f
➪ (equal? ‘(1 2) ‘(2 1)) ⇒ #f
➪ (equal? ‘(1 2 3) ‘(1 (2 3))) ⇒ #f
➪ (equal? ‘() ‘()) ⇒ #t
➪ (equal? 1 1.0) ⇒ #f
• Other kinds of equality: string=?, eqv?, eq?, etc.
➪ (define L ‘(a b c))
➪ (eq? L L) ⇒ #t
➪ (eq? ‘(a b c) ‘(a b c)) ⇒ #f
➪ (equal? ‘(a b c) ‘(a b c)) ⇒ #t
CWRU/EECS345/Beer
Scheme: List Processing
• Putting lists together
➪ (cons ‘a ‘(b c)) ⇒ (a b c)
➪ (append ‘(a) ‘(b c)) ⇒ (a b c)
➪ (list ‘a ‘b ‘c) ⇒ (a b c)
• Challenges
➪ (cons ‘() ‘())
⇒ (())
➪ (append ‘() ‘())
⇒ ()
➪ (list ‘() ‘())
⇒ (()())
CWRU/EECS345/Beer
Scheme: List Processing
• Taking lists apart
➪ (car ‘(a b c)) ⇒ a (first)
➪ (cdr ‘(a b c)) ⇒ (b c) (rest)
• Challenges
➪ (car (cdr ‘(a b c)))
⇒b
➪ (cdr (car ‘(a b c)))
⇒ ERROR
➪ (define L ‘(a (b (c))))
➪ (cons (car (car (cdr L))) (cdr (car (cdr L))))
⇒ (b (c))
(cdr L) ⇒ ((b (c)))
(car (cdr L)) ⇒ (b (c))
(car (car (cdr L))) ⇒ b
(cdr (car (cdr L))) ⇒ ((c))
CWRU/EECS345/Beer
Box and Arrow Notation
The list (A B C)
A B C
A B C
A B C
CWRU/EECS345/Beer
Box and Arrow Notation
(cons ‘A ‘(B C))
A B C
A B C
CWRU/EECS345/Beer
Box and Arrow Notation
(list ‘A ‘B ‘C)
A B C
(append ‘(A B) ‘(C))
A B C
CWRU/EECS345/Beer
User-Defined List Functions
(define (my-length l)
(if (null? l)
0
(+ 1 (my-length (cdr l)))))
(define (my-reverse l)
(if (null? l)
‘()
(append (my-reverse (cdr l)) (list (car l)))))
CWRU/EECS345/Beer
User-Defined List Functions
(replace ‘a ‘b ‘(a b c)) ⇒ (b b c)
(define (replace x y l)
(cond ((null? l) ‘())
((equal? x (car l))
(cons y (replace x y (cdr l))))
(#t (cons (car l) (replace x y (cdr l))))))
???
CWRU/EECS345/Beer
User-Defined List Functions
(define (replace x y l)
(cond ((null? l) ‘())
((equal? x (car l))
(cons y (replace x y (cdr l))))
((list? (car l))
(cons (replace x y (car l))
(replace x y (cdr l))))
(#t (cons (car l) (replace x y (cdr l))))))
CWRU/EECS345/Beer
Scheme: Local Bindings
(define (replace x y l)
(cond ((null? l) ‘())
((equal? x (car l))
(cons y (replace x y (cdr l))))
((list? (car l))
(cons (replace x y (car l))
(replace x y (cdr l))))
(#t (cons (car l) (replace x y (cdr l))))))
CWRU/EECS345/Beer
Scheme: Local Bindings
;;; Replace X with Y in L
;;; Version 3 - RDB
(define (replace x y l)
(if (null? l)
‘()
(let ((first (car l))
(rest (cdr l)))
(cond ((equal? x first)
(cons y (replace x y rest)))
((list? first)
(cons (replace x y first)
(replace x y rest)))
(#t (cons first (replace x y rest)))))))
CWRU/EECS345/Beer
Variables vs. Named Values
(let ((x 1))
(+ x x))
⇔ (+ 1 1)
{
⇔
{
int x = 1;
1 = 1 + 1;
x = x + x;
}
}
{ {
⇔
int x = 1, y = 1; int x, y;
x = x + x; x = 1 + 1;
y = x; y = 1;
} }
CWRU/EECS345/Beer
Symbolic Manipulation
(define (deriv expr var)
(cond
((number? expr) 0)
((equal? expr var) 1)
((symbol? expr) 0)
((equal? (car expr) ‘+)
(list ‘+
(deriv (car (cdr expr)) var)
(deriv (car (cdr (cdr expr))) var)))
...))
CWRU/EECS345/Beer
Higher-Order Functions
(define (double-list l)
(if (null? l)
‘()
(cons (+ (car l) (car l))
(double-list (cdr l)))))
(define (square-list l)
(if (null? l)
‘()
(cons (* (car l) (car l))
(square-list (cdr l)))))
CWRU/EECS345/Beer
Higher-Order Functions
(define (my-map fn l)
(if (null? l)
‘()
(cons (fn (car l))
(my-map fn (cdr l)))))
CWRU/EECS345/Beer
Let and Lambda
• Let is just syntactic sugar for lambda
(let ((a 1)
(b 2))
(+ a b))
⇔
((lambda (a b) (+ a b)) 1 2)
CWRU/EECS345/Beer
Higher-Order Functions
Function composition: f °g(x) = f(g(x))
(define (compose f g)
(lambda (x) (f (g x)))
(square-double 3) ⇒ 36
((compose square double) 3) ⇒ 36
CWRU/EECS345/Beer
Eval
• The evaluation problem revisited
➪ (+ 1 2) ⇒ 3
➪ ‘(+ 1 2) ⇒ (+ 1 2)
• We can turn this “problem” into a feature
• We can write programs to build other programs!
➪ (list ‘+ 1 2) ⇒ (+ 1 2)
➪ (define (build-call fn args) (cons fn args))
➪ (build-call ‘+ ‘(1 2)) ⇒ (+ 1 2)
• But how do we make these new programs execute?
• We need a kind of “inverse” of quote
(instead of stopping evaluation , we want an extra evaluation)
➪ (eval ‘(+ 1 2)) ⇒ 3
➪ (eval (list ‘+ 1 2)) ⇒ 3
• The basis of a powerful macro facility
CWRU/EECS345/Beer
Common Lisp
• Attempt to define a common core across many Lisp dialects
• First version developed from 1981-1984 (CLtL 1)
• ANSI working group formed in 1986
• Revised Common Lisp became an ANSI standard in 1994
• While Scheme is minimalist, Common Lisp is maximalist
• CLtL 2 is over 1000 pages long!
• LispWorks, Allegro Common Lisp, OpenMCL
CWRU/EECS345/Beer
CWRU/EECS345/Beer
From Scheme to Common Lisp
• Defining named globals
➪ (define PI 3.14159) vs. (defvar PI 3.14159)
➪ Also defconstant and defparameter
• Defining functions
➪ (define (f x) ...) vs. (defun f (x) ...)
• Predicate Names
➪ null? vs. null
➪ number? vs. numberp, etc.
➪ equal? vs. equal
• Invoking functions bound to symbols
➪ + vs. #’+
➪ (f x) vs. (funcall f x)
• The empty list: () vs. ()/nil
• Booleans: #t/#f vs. t/nil
CWRU/EECS345/Beer
Other Functional Languages
• ML
fun fact n =
if n = 0 then 1 else n * fact(n-1);
• Haskel
fact 0 = 1
fact n = n * fact(n-1)
• Dylan
define method fact(n)
if (n = 0) 1
else n * fact(n-1)
end
end;
CWRU/EECS345/Beer