Upload
others
View
26
Download
0
Embed Size (px)
Citation preview
1
Programming Languages
Session 5 – Main Theme
Functional Programming
Dr. Jean-Claude Franchitti
New York University
Computer Science Department
Courant Institute of Mathematical Sciences
Adapted from course textbook resources
Programming Language Pragmatics (3rd Edition)
Michael L. Scott, Copyright © 2009 Elsevier
2
22 Functional ProgrammingFunctional Programming
Agenda
11 Session OverviewSession Overview
33 ConclusionConclusion
3
What is the course about?
�Course description and syllabus:
» http://www.nyu.edu/classes/jcf/CSCI-GA.2110-001
» http://www.cs.nyu.edu/courses/summer14/CSCI-GA.2110-
001/index.html
�Textbook:» Programming Language Pragmatics (3rd Edition)
Michael L. Scott
Morgan Kaufmann
ISBN-10: 0-12374-514-4, ISBN-13: 978-0-12374-514-4, (04/06/09)
�Additional References:» Osinski, Lecture notes, Summer 2010
» Grimm, Lecture notes, Spring 2010
» Barrett, Lecture notes, Fall 2008
» Gottlieb, Lecture notes, Fall 2009
5
Icons / Metaphors
5
Common Realization
Information
Knowledge/Competency Pattern
Governance
Alignment
Solution Approach
6
Session 4 Review
� Subprogram
� Environment of the Computation
� Review of Stack Layout
� Calling Sequences
� Calling Sequences (C on MIPS)
� Parameter Passing
� Generic Subroutines and Modules
� First Class Functions
� Higher-Order Functions
� Block Structure
� Exception Handling
� Coroutines
� Conclusions
7
22 Functional ProgrammingFunctional Programming
Agenda
11 Session OverviewSession Overview
33 ConclusionConclusion
8
Functional Programming
� Historical Origins
� Lambda Calculus
� Functional Programming Concepts
� A Review/Overview of Scheme
� Evaluation Order Revisited
� High-Order Functions
� Functional Programming in Perspective
9
� Functional Programming refers to a programming style in which every procedure is functional, i.e. it computes a function of its inputs with no side effects
� Functional programming languages are based on this idea, but they also provide a number of interesting features that are often missing in imperative languages
� One of the most important and interesting features of functional programming languages is that functions are first-class values.
� The means that programs can create new functions at run-time
� This can be leveraged to build powerful higher-order functions: a higher-order function either takes a function as an argument or returns a function as a result (or both)
� Functional languages draw heavily on the lambda-calculus for inspiration
Historical Origins - Functional Programming
10
� The imperative and functional models grew out of
work undertaken Alan Turing, Alonzo Church,
Stephen Kleene, Emil Post, etc. ~1930s
» different formalizations of the notion of an algorithm, or
effective procedure, based on automata, symbolic
manipulation, recursive function definitions, and
combinatorics
� These results led Church to conjecture that any
intuitively appealing model of computing would be
equally powerful as well
» this conjecture is known as Church’s thesis
Historical Origins
11
� Turing’s model of computing was the
Turing machine a sort of pushdown
automaton using an unbounded storage
“tape”
» the Turing machine computes in an
imperative way, by changing the values in
cells of its tape – like variables just as a high
level imperative program computes by
changing the values of variables
Historical Origins
12
� Church’s model of computing is called the
lambda calculus
» based on the notion of parameterized expressions
(with each parameter introduced by an occurrence of
the letter λ—hence the notation’s name.
» Lambda calculus was the inspiration for functional
programming• basis for functional languages (e.g., Lisp, Scheme, ML, Haskell)
• typed and untyped variants
• has syntax and reduction rules
» one uses it to compute by substituting parameters into
expressions, just as one computes in a high level
functional program by passing arguments to functions
Historical Origins
13
� Mathematicians established a distinction between
» constructive proof (one that shows how to obtain a
mathematical object with some desired property)
» nonconstructive proof (one that merely shows that
such an object must exist, e.g., by contradiction)
� Logic programming is tied to the notion of
constructive proofs, but at a more abstract level:
» the logic programmer writes a set of axioms that allow
the computer to discover a constructive proof for each
particular set of inputs
Historical Origins
18
Lambda Calculus – Order of Evaluation – Does it Matter?
Church-Rosser Theorem
� If a term M can be reduced (in 0 or more steps) to terms N and P, then there exists a term Q such that both N and P can be reduced to Q.
19
Lambda Calculus – Order of Evaluation – Does it Matter?
� The property expressed in the Church-Rosser theorem is called confluence. We say that -reduction is confluent.
� Corollary
»Every term has at most one normal form.
� Why at most one?
»There are some terms with no normal form!
� Example:
20
Lambda Calculus – Computability
� The important notion of computability relies on a formal model of
computation.
� Many formal models have been proposed:
» 1. General recursive functions defined by means of an equation calculus
(G¨odel-Herbrand-Kleene)
» 2. -recursive functions and partial recursive functions (G¨odel-Kleene)
» 3. Functions computable by finite machines known as Turing machines
(Turing)
» 4. Functions defined from canonical deduction systems (Post)
» 5. Functions given by certain algorithms over a finite alphabet (Markov)
» 6. Universal Register Machine-computable functions (Shepherdson-Sturgis)
» 7. Any function you can program in your favorite programming language
� Fundamental Result
» All of these (and many other) models of computation are equivalent. That is,
they give rise to the same class of computable functions.
» Any such model of computation is said to be Turing complete.
21
Lambda Calculus – Computational Power
Fact: The untyped -calculus is Turing complete. (Turing, 1937)
� But how can this be?
»There are no built-in types other than
“functions” (e.g., no booleans, integers,
etc.)
»There are no loops
»There are no imperative features
»There are no recursive definitions
22
Lambda Calculus – Numbers and Numerals
� number: an abstract idea
� numeral: the representation of a number
»Example: 15, fifteen, XV, 0F
• These are different numerals that all represent
the same number.
� Alien numerals:
» frobnitz − frobnitz = wedgleb
»wedgleb + taksar = ?
23
Lambda Calculus – Booleans in the Lambda-Calculus
� How can a value of “true” or “false” be
represented in the -calculus?
� Any way we like, as long as we define all the
boolean operations correctly.
� One reasonable definition:
» true takes two values and returns the first
» false takes two values and returns the second
28
� Functional languages such as Lisp,
Scheme, FP, ML, Miranda, and Haskell
are an attempt to realize Church's
lambda calculus in practical form as a
programming language
� The key idea: do everything by
composing functions
»no mutable state
»no side effects
Functional Programming Concepts
29
� Necessary features, many of which
are missing in some imperative
languages
»1st class and high-order functions
»serious polymorphism
»powerful list facilities
»structured function returns
»fully general aggregates
»garbage collection
Functional Programming Concepts
30
� So how do you get anything done in a functional language?
»Recursion (especially tail recursion) takes
the place of iteration
» In general, you can get the effect of a series of assignments
x := 0 ...
x := expr1 ...
x := expr2 ...
from f3(f2(f1(0))), where each f
expects the value of x as an argument, f1
returns expr1, and f2 returns expr2
Functional Programming Concepts
31
� Recursion even does a nifty job of replacing looping
x := 0; i := 1; j := 100;
while i < j do
x := x + i*j; i := i + 1;
j := j - 1
end while
return x
becomes f(0,1,100), wheref(x,i,j) == if i < j then
f (x+i*j, i+1, j-1) else x
Functional Programming Concepts
32
� Thinking about recursion as a direct, mechanical replacement for iteration, however, is the wrong way to look at things
»One has to get used to thinking in a recursive
style
� Even more important than recursion is the notion of higher-order functions
»Take a function as argument, or return a
function as a result
»Great for building things
Functional Programming Concepts
33
� Lisp also has (these are not necessary present in other functional languages)»homo-iconography
» self-definition
» read-evaluate-print
� Variants of LISP»Pure (original) Lisp
» Interlisp, MacLisp, Emacs Lisp
»Common Lisp
»Scheme
Functional Programming Concepts
34
� Pure Lisp is purely functional; all other Lisps have imperative features
� All early Lisps dynamically scoped»Not clear whether this was deliberate or if it
happened by accident
� Scheme and Common Lisp statically scoped»Common Lisp provides dynamic scope as an
option for explicitly-declared special functions
»Common Lisp now THE standard Lisp• Very big; complicated (The Ada of functional
programming)
Functional Programming Concepts
35
� Scheme is a particularly elegant Lisp
� Other functional languages
»ML
»Miranda
»Haskell
»FP
� Haskell is the leading language for
research in functional programming
Functional Programming Concepts
36
� As mentioned earlier, Scheme is a particularly elegant Lisp» Interpreter runs a read-eval-print loop
»Things typed into the interpreter are evaluated (recursively) once
»Anything in parentheses is a function call (unless quoted)
»Parentheses are NOT just grouping, as they are in Algol-family languages
• Adding a level of parentheses changes meaning
(+ 3 4) ⇒ 7
((+ 3 4))) ⇒ error
(the ' ⇒' arrow means 'evaluates to‘)
Functional Programming Concepts
37
� A closure is a function whose execution has access to an external environment
� LISP was the earliest language to do closures, and it did them the other way (dynamic)
� Static generally considered better; Scheme is basically LISP with closures done “right”
Functional Programming Concepts - Closures
38
� related to Lisp, first description in 1975
� designed to have clear and simple semantics (unlike
Lisp)
� statically scoped (unlike Lisp)
� dynamically typed
» types are associated with values, not variables
� functional: first-class functions
� garbage collection
� simple syntax; lots of parentheses
» homogeneity of programs and data
� continuations
� hygienic macros
A Review/Overview of Scheme
41
A Review/Overview of Scheme – Standard Predicates
� If variables do not have associated types, we
need a way to find out what a variable is
holding:
» symbol?
» number?
» pair?
» list?
» null?
» zero?
� Different dialects may have different naming
conventions, e.g., symbolp, numberp, etc.
42
� Scheme (continued):
» Quoting
(+ 3 4) ⇒ 7
(quote (+ 3 4)) ⇒ (+ 3 4)
'(+ 3 4) ⇒ (+ 3 4)
A Review/Overview of Scheme
» Mechanisms for creating new scopes(let ((square (lambda (x) (* x x))) (plus +))
(sqrt (plus (square a) (square b))))
let*
letrec
43
� Scheme (continued):
» Conditional expressions(if (< 2 3) 4 5) ⇒ 4
(cond
((< 3 2) 1)
((< 4 3) 2)
(else 3)) ⇒ 3
» Imperative stuff
• assignments
• sequencing (begin)
• iteration
• I/O (read, display)
A Review/Overview of Scheme
46
� Scheme standard functions (this is not a complete list):
»arithmetic
»boolean operators
»equivalence
» list operators
» symbol?
»number?
» complex?
» real?
» rational?
» integer?
A Review/Overview of Scheme
48
� a number evaluates to itself
� an atom evaluates to its current binding
� a list is a computation:
»must be a form (e.g., if, lambda), or
» first element must evaluate to an operation
» remaining elements are actual parameters
» result is the application of the operation to the
evaluated actuals
A Review/Overview of Scheme – Rules of Evaluation
55
A Review/Overview of Scheme – Locals
Basic let skeleton:
( let
((v1 init1) (v2 init2) ... (vn initn))
body)
To declare locals, use one of the let variants:
� let : Evaluate all the inits in the current environment; the vs are bound to fresh locations holding the results.
� let* : Bindings are performed sequentially from left to right, and each binding is done in an environment in which the previous bindings are visible.
� letrec : The vs are bound to fresh locations holding undefined values, the inits are evaluated in the resulting environment (in some unspecified order), each v is assigned to the result of the corresponding init. This is what we need for mutually recursive functions.
56
A Review/Overview of Scheme – Tail Recursion
� “A Scheme implementation is properly tail-recursive if it supports an unbounded number of active tail calls.”
57
A Review/Overview of Scheme - Continuations
� A procedure with parameters to tell you what to do with an answer after computing it
� In Continuation Passing Style (CPS), instead of returning a result, you pass the result to the continuation that was passed to you
(define (mysqrt x) (sqrt x)) ; normal definition of mysqrt(display (mysqrt 4)) ; normal call to display mysqrt of 4(+ (mysqrt 4) 2) ; normal call to compute sqrt(4) + 2
(define (mysqrt x k) (k (sqrt x))) ; CPS definition of mysqrt(mysqrt 4 display) ; CPS call to display mysqrt of 4(mysqrt 4 (lambda (x) (+ x 2))) ; CPS call to compute sqrt(4) + 2
(define (factorial n) (if (<= n 1) 1 (* n (factorial (- n 1))))) ; normal definition of factorial
(+ (factorial 4) 2) ; normal call to compute 4! + 2
(define (factorial n k) (if (<= n 1) (k 1) (factorial (- n 1) (lambda (ret) (k (* n ret))))))(factorial 4 (lambda (x) (+ x 2))) ; CPS call to compute 4! + 2
58
� To return multiple values: x, y, z, instead of packing it into a single object like (x.(y.z)), and later unpacking, simply call the continuation passing x, y, z
� Instead of alternate exits (e.g. succeed and return x, y, z vs. fail and return nothing), use multiple continuations
A Review/Overview of Scheme – Using CPS and Closures
59
A Review/Overview of Scheme – Using CPS and Closures (Example)
(define get-expr-prefix (lambda (
lst ; a list of unparsed tokens
success ; continuation(parsedexpr unparsed) parsedexpr : expr-tree of prefix of lst, unparsed : rest
failure ; continuation(unparsed)
)
(letrec (make-closure (lambda (parsedexpr ; parsedexpr : expr parsed so far
cont ; a continuation(handle-has-suffix, handle-has-no-suffix)
; handle-has-suffix: lambda(operator unparsed)
; handle-has-no-suffix : lambda(unparsed)
)
(letrec ((expr parsedexpr)
(handle-has-suffix-method (lambda (operator unparsed …)
(handle-has-no-suffix-method(lambda (unparsed) (success parsedexpr unparsed))
(cont handle-has-suffix-method handle-has-no-suffix-method) ))
(
(term-found (lambda (parsedterm unparsed) ; parsedterm :term-tree, unparsed: rest of lst
(make-closure (cons ‘expr (list parsedterm))
(lambda (has-suffix-method has-no-suffix-method)
(get-additive-operator unparsed has-suffix-method has-no-suffix-method)))
(term-not-found (lambda (unparsed) (failure unparsed)))
(get-term-prefix lst term-found term-not-found) ) )))
(define get-expr (lambda lst)
(get-expr-prefix lst (lambda (parsed, unparsed) (if (empty? unparsed) (display parsed) (display ‘error))
(lambda(unparsed) (display ‘error))
; Find the term following the +/- operator
; If there is one, extend the expression, save it in the closure,
; and once again try to find a suffix
; If there isn’t one, this is a bad expression, e.g. A * B + )
letrec (got-term (lambda (parsedterm rest)
(make-closure (cons ‘expr (list parsedexpr operator parsedterm))
(lambda (has-suffix has-no-suffix)
(get-additive-operator rest has-suffix has-no-suffix))))
(no-term (lambda rest) (failure rest)))
(get-term-prefix rest got-term no-term))
60
(call-with-current-continuation
(lambda (exit)
(for-each (lambda (x) (if (negative? x) (exit x)))
'(54 0 37 -3 245 19)) #t)) => -3
(define list-length (lambda (obj)
(call-with-current-continuation
(lambda (return)
(letrec ((r (lambda (obj)
(cond ((null? obj) 0) ((pair? obj) (+ (r (cdr obj)) 1))
(else (return #f))))))
(r obj))))))
(list-length '(1 2 3 4)) => 4
(list-length '(a b . c)) => #f
A Review/Overview of Scheme – Call / CC by Example
61
Macros
� These are ways to extend Scheme
� They are invoked on static expressions rather than on values
� Scheme introduced “hygienic macros”
» Hygiene: If a macro transformer inserts a binding for
an identifier, the new binding will not capture other
identifiers of the same name introduced elsewhere.
» Referential Transparency: If a macro transformer
inserts a free reference to an identifier, the reference
refers to the binding that was visible where the
transformer was specified, regardless of any local
bindings that may surround the use of the macro.
62
Eval
� This allows you to create an arbitrary expression representing a Scheme program as a data object, and then execute it
� There is an interpreter that one can write in scheme that emulates eval: what is surprising is how short the program is – a Scheme interpreter in Scheme is a shorter program than your homework program!
63
A Review/Overview of SchemeApplicative Example
(define isort (lambda (l) (letrec ( ; defines a list of bindings (here just 1)
(insert ( ; inserts item x in sorted order to list llambda (x l) (if (null? l) (list x) (if (<= x (car l)) (cons x l) (cons (car l) (insert x (cdr l)))) ; means this insert
)))); the below is executed in the context of the bindings
(if (null? l) nil (insert (car l) (isort (cdr l)))))
)) (isort (3 20 13 2))
64
� We'll invoke the program by calling a function
called 'simulate', passing it a DFA description and
an input string
» The automaton description is a list of three items:
• start state
• the transition function
• the set of final states
» The transition function is a list of pairs
• the first element of each pair is a pair, whose first element is a state and whose second element in an input symbol
• if the current state and next input symbol match the first element of a pair, then the finite automaton enters the state given by the second element of the pair
A Review/Overview of SchemeExample program - Simulation of DFA
67
� Applicative order
»what you're used to in imperative languages
»usually faster
� Normal order
» like call-by-name: don't evaluate arg until you
need it
» sometimes faster
» terminates if anything will (Church-Rosser
theorem)
Evaluation Order Revisited
68
� In Scheme
» functions use applicative order defined with lambda
» special forms (aka macros) use normal order defined
with syntax-rules
� A strict language requires all arguments to be
well-defined, so applicative order can be used
� A non-strict language does not require all
arguments to be well-defined; it requires
normal-order evaluation
Evaluation Order Revisited
69
� Lazy evaluation gives the best of
both worlds
� But not good in the presence of side
effects.
»delay and force in Scheme
»delay creates a "promise"
Evaluation Order Revisited
70
� Higher-order functions
»Take a function as argument, or return a function as a result
»Great for building things
»Currying (after Haskell Curry, the same guy Haskell is named after)
• For details see Lambda calculus on CD
• ML, Miranda, and Haskell have especially
nice syntax for curried functions
High-Order Functions
71
� Advantages of functional languages
» lack of side effects makes programs easier to
understand
» lack of explicit evaluation order (in some languages)
offers possibility of parallel evaluation (e.g. MultiLisp)
» lack of side effects and explicit evaluation order
simplifies some things for a compiler (provided you
don't blow it in other ways)
» programs are often surprisingly short
» language can be extremely small and yet powerful
Functional Programming in Perspective
72
� Problems
» difficult (but not impossible!) to implement efficiently
on von Neumann machines
• lots of copying of data through parameters
• (apparent) need to create a whole new array in order to change one element
• heavy use of pointers (space/time and locality problem)
• frequent procedure calls
• heavy space use for recursion
• requires garbage collection
• requires a different mode of thinking by the programmer
• difficult to integrate I/O into purely functional model
Functional Programming in Perspective
73
� 1st class procedures
� Dynamically created procedures
� Based on lambda calculus over atoms and pairs; by
convention, lists are pairs <head, rest>
� Continuations
� Automatic garbage collection
� Applicative style: binding, no update, no side effects (but
there are exceptions to this, the “set!” operator)
� Static scoping, but no static typing!
� Simple syntax
» (afunction arg1 arg2 …) ; function application
» Special forms, e.g. (if … ) (quote (x y)) (lambda (x) (* x x))
Functional Programming in Perspective – Essential Features in Scheme
74
22 Functional LanguagesFunctional Languages
Agenda
11 Session OverviewSession Overview
33 ConclusionConclusion
75
Assignments & Readings
� Readings
» Chapter Section 10 including 10.6.1 on the CD
� Programming Assignment #2
» See Programming Assignment #2 posted under “handouts” on the course Web site
» Due on July 24, 2014