Com S 342 meeting -*- Outline -*- * procedures (Little Schemer, Chapter 8) ** as data are values in Scheme ------------------------------------------ PROCEDURES Values + abstract: partial mappings from a domain to a range with side-effects on store + external (printed) form: in SCM: # # in Chez Scheme: # in Dr. Scheme: # or # Operations + procedures: apply + special forms: lambda, ______ ------------------------------------------ output syntax not standard ... ( ) equations: (f e1 e2 ...) = (apply f (list e1 e2 ...)) ((lambda (x1 x2 ... xn) b) e1 e2 ... en) = (let ((x1 e1) (x2 e2) ... (xn en)) b) ** 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++ and Java are related to this, as we'll see... C# delegates are also very similar ------------------------------------------ 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 (deftype one number) (define one 1) (deftype add1 (-> (number) number)) (define add1 (lambda (n) (+ n one))) ------------------------------------------ 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 ** 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 ------------------------------------------ ... named, 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/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 (deftype compose (forall (S T U) (-> ((-> (T) U) (-> (S) T)) (-> (S) U)))) (define compose (lambda (f g) (lambda (x) (f (g x))))) ------------------------------------------ FOR YOU TO DO Write a procedure twice (deftype twice (forall (T) (-> ((-> (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)) ------------------------------------------ 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))) ;; TYPE: (forall (T) ;; (-> ((list-of T) (list-of T) ;; (list-of T)) ;; (list-of T))) curried form: (lambda (ls1) (lambda (ls2) (lambda (ls3) (append ls1 (append ls2 ls3))))) ;; TYPE: (forall (T) ;; (-> ((list-of T)) ;; (-> ((list-of T)) ;; (-> ((list-of T)) ;; (list-of T))))) procedure: (lambda (x y) (+ x y)) ;; TYPE: (-> (number number) ;; number) curried form: (lambda (x) (lambda (y) (+ x y))) ;; TYPE: (-> (number) ;; (-> (number) ;; number)) ------------------------------------------ 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() { cout << (cadd(2))(3) << endl; } ------------------------------------------ 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); class closure { public: closure(int x_val, func f_val) : x(x_val), f(f_val) {} int call(int arg) { return f(x, arg); } private: const int x; const func f; }; int add(int x, int y) { return x + y; } closure* cadd(int x) { return new closure(x, add); } int main() { cout << cadd(2)->call(3) << endl; } ------------------------------------------ explain what a closure is: environment (values for free vars) + code (This is one reason we don't use C/C++, it's too hard to do this...) Q: So, in general, 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. ** 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) (deftype sum (-> (number ...) number)) (define sum (deftype sum-of-list (-> ((list-of number)) number)) (define sum-of-list (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: (deftype product (-> (number ...) number)) (product ) ==> 1 (product 3) ==> 3 (product 4 3) ==> 12 (product 7 6 5 4) ==> 840 (deftype list (forall (T) (-> (T ...) (list-of 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... --------------------------------------------------------- UNRESTRICTED LAMBDA AND APPLY ((lambda args (f args)) e1 e2 ...) = (f (list e1 e2 ...)) (apply f (list e1 e2 ...)) = (f e1 e2 ...) So, for all procedures f and lists ls: (apply (lambda args (f args)) ls) = (f ls) ---------------------------------------------------------