Com S 342 meeting -*- Outline -*- * procedures (1.3) ** as data are values in Scheme ------------------------------------------ PROCEDURES (1.3) Values + abstract: partial mappings from a domain to a range with side-effects on store + syntax of printed output: ? in SCM: # # Operations + procedures: apply + special forms: lambda, ______ ------------------------------------------ output syntax not standard ... ( ) ** creation show on computer ------------------------------------------ LAMBDA MAKES PROCEDURE VALUES examples: (lambda (n) (+ n 1)) (lambda (x y) (/ (+ x y) 2)) terminology: - closure - formal, formal parameter, bound variable - anonymous procedure ------------------------------------------ ... = procedure value = envirionment + code Q: What other langauges use anonymous procedures? ML, Haskell, LISP, but these are all similar kinds... Smalltalk blocks are like this an object in C++ is related to this, as we'll see... ------------------------------------------ USE OF ANONYMOUS PROCEDURES > ((lambda (n) (+ n 1)) 3) 4 > (map (lambda (n) (+ n 10)) '(4 5 6)) (14 15 16) ------------------------------------------ We'll see other uses... Most languages require procedures to be named (avoids overhead of the general case of anonymous procedures) ** naming Can name procedures with define ------------------------------------------ NAMING PROCEDURES (define one ;; TYPE: number 1) (define add1 ;; TYPE: (-> (number) number) (lambda (n) (+ n one))) (define map ;; TYPE: (-> ((-> (S) T) (list S)) ;; (list T)) (lambda (proc ls) (if (null? ls) '() (cons (proc (car ls)) (map proc (cdr ls)))))) ------------------------------------------ Note how the syntax is just like defining any other value Have them define a procedure to: multiply an argument by 2 return the second item in a list Q: What's this an example of? regularity ** first-class values ------------------------------------------ FIRST-CLASS VALUES def: a value is *first-class* if it can be Examples of types with first-class values: type FORTRAN C Scheme number boolean array record pointer procedure ------------------------------------------ ... given to an returned from procedures, and stored in data structures (just like numbers). Q: Are procedures first-class values in FORTRAN? C? Scheme? have pointers to procedures in C, so pointers to procs are first-class Q: Are arrays first-class values in C? no All values in Scheme are first-class, another instance of regularity. *** uses of first-class procedures recall the map function, captures common pattern on lists Similarly, other languages have sort procedures, that take ordering as a parameter ------------------------------------------ AN EXAMPLE: COMPOSE Want procedure ((compose car cdr) '(a b c)) = b = (car '(b c)) = (car (cdr '(a b c))) ((compose not null?) '()) = #f = (not (null? '())) ((compose not zero?) x) = (not (zero? x)) In general: ((compose f g) x) = (f (g x)) How to write this: ------------------------------------------ Show how to write this: (compose f g) is a procedure of one argument, x, so it must be (lambda (x) (f (g x))) But compose itself is a procedure, of two arguments, f and g, and when called it returns this other procedure, so... (lambda (f g) (lambda (x) (f (g x)))) And this has to be named compose (define compose (lambda (f g) (lambda (x) (f (g x))))) Explain the type (define compose ;; TYPE: (-> ((-> (T) U) (-> (S) T)) ;; (-> (S) U)) (lambda (f g) (lambda (x) (f (g x))))) ------------------------------------------ FOR YOU TO DO Write a procedure twice with type: (-> ((-> (T) T)) (-> (T) T)) Examples: ((twice not) #t) = #t = (not (not #t)) ((twice (lambda (n) (+ n 1))) 3) = 5 = ((lambda (n) (+ n 1)) ((lambda (n) (+ n 1)) 3)) ((twice car) '((3 4))) = 3 = (car (car '((3 4)))) ------------------------------------------ Hints: What's the general formula? *** currying (pp. 26-28) Named after Haskell Curry, a logician (although it was actually invented by Frege and Schoenfinkel) ------------------------------------------ CURRYING procedure: (lambda (ls1 ls2 ls3) (append ls1 (append ls2 ls3))) curried form: (lambda (ls1) (lambda (ls2) (lambda (ls3) (append ls1 (append ls2 ls3))))) procedure: (lambda (x y) (+ x y)) curried form: (lambda (x) (lambda (y) (+ x y))) ------------------------------------------ Q: How is the type of the curried form related to the original type? give it a name, show how to use it. Can this be done in C? ------------------------------------------ CURRYING IN C? #include typedef int (*func)(int); int takes_y(int y) { return(x + y); } func cadd(int x) { return(&takes_y); } int main() { printf("%i\n", (cadd(2))(3)); } ------------------------------------------ Q: does this work? no, what's the value of x in takes_y? To solve that problem, simulate the notion of a closure ------------------------------------------ CORRECTED C PROGRAM #include typedef int (*func)(int, int); typedef struct { int x; func f; } closure; typedef closure *closurePtr; int add(int x, int y) { return x + y; } closurePtr cadd(int x) { closurePtr c; c = (closurePtr)malloc(sizeof(closure)); c->f = add; c->x = x; return c; } int call_closure(closurePtr c, int arg) { return (c->f)(c->x, arg); } int main() { printf("%i\n", call_closure(cadd(2), 3)); } ------------------------------------------ explain what a closure is: environment (values for free vars) + code (This is one reason we don't use C, it's too hard to do this...) Q: What in C++ is like a closure? an object: it has a little environment (data members) and code (member functions) But again, in C++, don't have anonymous classes, and can't capture the environment at run-time without preparing with class definition ahead of time. We'll come back to this later. ** uses of curried procedures see my TR on this subject ------------------------------------------ GRAVITATIONAL FORCE EXAMPLE (define G ;; TYPE: N * m^2 / kg^2 6.670e-11) (define square ;; TYPE: (-> (m) m^2) (lambda (r) (* r r))) (define grav-force ;; TYPE: (-> (kg m kg) N) (lambda (m1 r m2) (if (zero? r) 0.0 (/ (* G (* m1 m2)) (square r))))) (define grav-force-c ;; TYPE: (-> (kg) ;; (-> (m) ;; (-> (kg) ;; N))) (if (zero? r) 0.0 (/ (* G (* m1 m2)) (square r)))) ------------------------------------------ ...(lambda (m1) ; kg (lambda (r) ; m (lambda (m2) ; kg (if (zero? r) ------------------------------------------ USING IT (define mass-of-earth ;; TYPE: kg 5.96e24) (define radius-of-earth ;; TYPE: m 6.37e6) (define earths-force ;; TYPE: (-> (grav-force-c mass-of-earth)) (define force-at-surface ;; TYPE: (-> (earths-force radius-of-earth)) > ;;; force in N on me (force-at-surface 77) > ;;; force in N on 1kg (a liter of coke) (force-at-surface 1) ------------------------------------------ ... 754 ... 9.797 Q: how would you compute forces at distance of moon's orbit by earth? Q: how would you compute forces exerted by the sun? Moral: curried functions can be used to preplan convenient tools so A CURRIED FUNCTION IS A TOOL-MAKER! ** variable arity procedures (1.3.3) ------------------------------------------ VARIABLE ARITY PROCEDURES (1.3.3) def: arity = the number of arguments a procedure can take Examples built-in to Scheme: Special form in Scheme: (lambda x body) (define sum ;; TYPE: (-> (number ...) number) (define sum-of-list ;; TYPE: (-> ((list number)) number) (lambda (lon) (if (null? lon) 0 (+ (car lon) (sum-of-list (cdr lon)))))) Semantics - caller's arguments made into list - the formal denotes a list in the body ------------------------------------------ ... list, vector, string, +, *, ... ... (lambda args (sum-of-list args))) Q: What in C is like this? printf, varargs Q: does a language have to have these? no, could force you to pass a list or vector, it's a convenience Q: does language have to allow you to write them yourself? no, but doing so makes the language more regular. It also satisfies the "automation" principle, in that it automatates a tedious or error-prone task These are also called "unrestricted procedures" ------------------------------------------ FOR YOU TO DO Implement the following: product: (-> (number ...) number) (product ) ==> 1 (product 3) ==> 3 (product 4 3) ==> 12 (product 7 6 5 4) ==> 840 list: (-> (T ...) (list T)) (list ) ==> () (list 'a) = (a) (list 'a 'b) = (a b) ------------------------------------------ Q: Does list have to be built-in to Scheme? shows the way you can pare a language back to its core...