Programming Languages Week 4 Functional programming in LISP College of Information Science and Engineering Ritsumeikan University
review of part 3 enumeration of dictionaries you receive a sequence of the keys easy to convert them to values or associations dictionary association list { a :1, b :2} [( a,1), ( b :2)] Python syntax for functional programming map(f, sequence) [f(x) for x in sequence] filter(f, sequence) [x for x in sequence if f(x)] multiple return values def nts(x): return x, x+x, x*x number, twice, squared = nts(10) print number, twice, squared #=> 10 20 100 generators yield instead of return lazy evaluation compute values only when they are actually needed 2
a functional programming language 1948 first electronic computer, programmed in machine code (1 st -generation languages) 1949 programmers use assembly language (second-generation languages) 1952 first high-level language: Autocode (third-generation languages) 1954 John Backus invents FORTRAN, first widely-used high-level language 1955 Grace Hopper invented FLOW-MATIC, eventually leading to COBOL 1958 John McCarthy invents LISP, the LISt Processing language, and the first dynamically-typed, functional, symbolic language language to have automatic memory management (garbage collection) language able to extend its own syntax and semantics language useful for Artificial Intelligence given the historical context, the achievements of LISP were miraculous maybe largely due to it being based on mathematical principles 3
components of LISP programs atoms simple names and values numbers: 1 3. 14 15 3e9 strings: "hello world" "abc" "" symbols: hello goodbye + -, &! #t #f #nil lists sequences of zero or more atoms or lists enclosed in parentheses the empty list: () one-dimensional list: ( 1 2 a b "last") two-dimensional (nested) list: ( 1 (2 a b) "last") 4
meaning of LISP programs an expression is either an atom, or a list of expressions all expressions are evaluated when they are encountered during program execution evaluating atoms: symbols name variables and evaluate to their currently bound value other atoms (numbers and strings) evaluate to themselves (they are literals) evaluating lists: a list is evaluated as a function call apply the value of their first element (a function) to the values of the remaining elements (the arguments) try these expressions now in Guile (press Enter at the end of each line): 3 4 + + 3 4 (+ 3 4) (+ 3 (* 4 5)) 5
some useful functions all the usual mathematical operators, including: + - * / < <= = >= > that work correctly, whenever possible, with any number of arguments try these: (+ 1 2 3 4 5) (* 1 2 3 4 5) (display value) prints value in a friendly form; e.g., strings are not surrounded by " characters (write value) prints value in the same form you would use to write it as a literal note that neither write nor display print a newline (newline) prints a newline try these... (display "hello\n") (write "hello\n") 6
everything is an expression there are no statements in LISP (if #t 2 3) ; an if expression (if #f 2 3) the two values called #t and #f represent true and false when symbols are evaluated, they are treated as variable identifiers the value that they are bound to is looked up if they are not bound to a value, an unbound variable error occurs try this expression: kaboom 7
defining and setting variables variables (define name value) creates a new variable called name and binds value to it (set! name value) re-binds name to a new value conditionals and loops (if condition consequent alternate) if condition is #t then consequent is evaluated, otherwise alternate (while condition consequent1 consequent2...) repeatedly evaluates all the consequents as long as the condition remains #t for example: (define x 10) (while (> x 0) (display x) (newline) (set! x (- x 1))) 8
anonymous functions anonymous functions (closures) are created with lambda (lambda (parameters...) expressions...) creates a closure (anonymous function) which, when called, evaluates the expressions in a context where successive parameter symbols are bound to successive actual arguments try these: (lambda () 42) ; an anonymous function ((lambda () 42)) ; which can be called with zero arguments (lambda (x) (+ x x)) ; function of one argument ((lambda (x) (+ x x)) 21) ; called with one argument (define double (lambda (x) (+ x x))) ; same function, bound globally (double 21) 9
function definitions function definitions are so common that they have a shorthand syntax (define square (lambda (x) (* x x))) can be written (define (square x) ; written exactly the way it is called (* x x)) ; the lambda is implicit, but still there (square 8) ; => 64 10
literal data the quote function returns its argument unevaluated (quote value) returns value without evaluating it try these: (+ 3 4) (quote (+ 3 4)) quote is so useful that it has a shorthand notation using a single quote character (+ 3 4) (+ 3 4) ; same as (quote (+ 3 4)), which is... (+ 3 4) ;... easily proven ;-) 11
map, filter, reduce Guile has built-on map and filter (working like the Python ones) (define double (lambda (x) (+ x x))) (map double (1 2 3)) ; => (2 4 6) (define even? (lambda (x) (= 0 (remainder x 2)))) (filter even? (0 1 2 3 4 5)) ; => (0 2 4) there is no built-in reduce, but it is easy to define and most arithmetic operators accept any number of arguments 12
lists (car list) returns the head (first element) of list (cdr list) returns the tail (all elements after the first) of list (car (1 2 3)) ; => 1 (cdr (1 2 3)) ; => (2 3) (cadr (1 2 3)) ; => 2 - same as (car (cdr (1 2 3))) (cddr (1 2 3)) ; => (3) - same as (cdr (cdr (1 2 3))) etc... 13
lists (length list) returns the number of elements in list (append lists...) concatenates all the lists to make a new list (list values...) makes a new list containing all the values (length ()) ; => 0 (length (a b c d)) ; => 4 (append (a b) (c d)) ; => (a b c d) (append (a b) (list 1 2 3)) ; => (a b 1 2 3) 14
quasiquotation quoted data is exactly as written (+ 3 (* 4 5)) ; => (+ 3 (* 4 5)) using a backquote instead of a forward quote (+ 3 (* 4 5)) ; => (+ 3 (* 4 5)) turns evaluation back on for any elements in the list preceded by a comma (+ 3,(* 4 5)) ; => (+ 3 20) this is useful for writing mostly-literal data with some computed parts 15
metaprogramming a metaprogram is a program that manipulates programs this is especially easy in LISP, because programs and data are the same thing define-macro defines a function that is called immediately, while an expression is being read in does not evaluate its arguments when the function is called, the parameters are bound to the data structure representing the expression 16
metaprogramming a function that squares its argument: (define (squared x) (display "squaring ") (display x) (newline) (* x x)) (define (test n) (squared n)) (test 9) ; => squaring 9 ; => 81 the message "squaring 9" is printed when text is evaluated 17
metaprogramming a function that squares its argument: (define (squared x) (display "squaring ") (display x) (newline) (*,x,x)) (define (test n) (squared n)) (test 9) ; => squaring 9 ; => (* 9 9) the function now returns an expression to calculate the desired result 18
a macro that squares its argument: (define-macro (squared x) (display "squaring ") (display x) (newline) (*,x,x)) metaprogramming (define (test n) (squared n)) ; => squaring n (test 9) ; => (* 9 9) the macro is executed during the definition of test and the result (* n n) used in place of (squared n) the definition of test is actually (define (test n) (* n n)) define-macro lets the programmer invent new syntax, control constructs, etc. 19
metaprogramming example imagine (unless x y z) which works like if, except that y is evaluated if x is false z is evaluated if x is true (define-macro (unless x y z) (if (not,x),y,z)) (if #t 1 2) ; => 1 (unless #t 1 2) ; => 2 (unless #f 1 2) ; => 1 metaprogramming is used to create domain-specific mini-languages within LISP new control constructs, data types, etc. designed to be effective for a specific problem domain programming becomes a two-stage process: design an optimal language to solve your problem, and implement it in LISP solve your problem by writing a program in your optimal language this is called metalinguistic abstraction 20
local variables are introduced using let local variables and blocks (let ((a 3) (b 4)) ; create and initialise two local variables (display a) (newline) (display b) (newline) (+ a b)) ; => 7 grouping several expressions together (let () (display "hello\n") 42) ; => 42 which has its own shorthand notation (begin (display "hello\n") 42) ; => 42 that is useful, e.g., to provide multiple expressions in an if 21
functional programming resources Scheme (a modern dialect of Lisp) impure FP with an emphasis on metaprogramming and linguistic abstraction good for prototyping new language semantics the best book ever written about programming uses Scheme comes with a free, complete online course MIT Open Courseware Structure and Interpretation Of Computer Programs http://ocw.mit.edu/ocwweb/electrical-engineering-and-computer-science/ 6-001Spring-2005/CourseHome/index.htm many dialects, some popular ones include: Guile http://www.gnu.org/software/guile/ MIT Scheme http://www.gnu.org/software/mit-scheme Haskell pure FP that is a small step up from FP in Python syntax not too different from Python s list comprehension syntax overloaded functions through pattern matching good for your career: Haskell is quite widely used in industry aerospace, defense, finance, social web apps, hardware design,... 22