Com S 342 meeting -*- Outline -*- * Call by Reference (6.2) ** informal overview (using the indirect array model) *** motivation Generally, it's a good thing that a procedure can't change variables in its caller (although it can mutate objects passed). But there are times when you want to encapsulate a pattern of assignments to variables, and then you need... ------------------------------------------ CALL-BY-REFERENCE (WITH INDIRECT ARRAYS) MOTIVATION define swap = proc(x,y) let temp = 0 in begin temp := x; x := y; y := temp end; let a = 3; b = 4 in begin swap(a,b); print(a); print(b) end; ------------------------------------------ Draw picture of what happens to this expression in call-by-value show what we want in call by reference. Q: What does this in C++? use of & for a formal: void swap(int &x, int& y) { int temp = x; x = y; y = temp; } ------------------------------------------ FOR YOU TO DO Draw a picture of what happens with the following with call by reference, (using the indirect array model): define assign2 = proc(v1, v2, e1, e2) begin v1 := e1; v2 := e2 end; define swap4 = proc(x,y) assign2(x,y,y,x); let a = 3; b = 4 in begin swap4(a,b); print(a); print(b) end; ------------------------------------------ Q: Can you do make the above work in C++ or Pascal? How? use the first two parameters by reference, the others by value *** aliasing without call by reference, each variable has its own storage cell with call by reference this isn't true. ------------------------------------------ VARIABLE ALIASING define addInTo = proc(c,a,b) begin c := b; c := +(c,a) end; let x = 3; y = 4 in begin addInTo(x,x,y); x end; ------------------------------------------ Q: Can you draw pictures of how this executes with call by reference? Q: What does the last expression return? 8 Aliasing makes programs hard to reason about *** passing array elements Q: Can we use swap to swap array elements? ------------------------------------------ CALL BY REFERENCE FOR ARRAY ELEMENTS define init = proc(v) v := 0; letarray a[3] in begin init(a[0]); init(a[1]); init(a[2]); print(a[0]) end; ------------------------------------------ Draw picture of what happens *** passing non-variables by reference Old FORTRAN joke: how do you change 2 to be 3? SUBROUTINE F(X) X = 3 RETURN END CALL F(2) PRINT 20, 2+2 20 FORMAT I10 ------------------------------------------ WHAT IF PASS A VALUE BY REFERENCE? define c = 3; define p = proc (x) x := 5; begin p(add1(c)); c end; ------------------------------------------ Q: What are the choices? give an error (at compile time?) make it work wrong (modify location containg constant) or right (allocate a location for the value, pass that) ** model (interpreter/language changes) for call-by-reference, indirect arrays *** what kinds of parameters Q: What kinds of things can we pass by reference? variables, array elements, values Think of passing a value as sugar for making up a new variable, so it boils down to variables and array elements *** domains and data structures What is passed in call-by reference is some kind of L-value; we won't use a new domain, called L-value, however, but just define denoted values to be these L-values ------------------------------------------ DOMAINS FOR CALL BY REFERENCE WITH INDIRECT ARRAYS Domains: Environment = -> Denoted-Value Denoted-Value = Expressed-Value = Number + Procedure + Array(Expressed-Value) Procedure = prim-proc + closure Array(T) = {Cell(T)}* New data structure: ------------------------------------------ ... Cell(Expressed-Value) + Array-Element(Expressed-Value) Q: How would we represent an array element L-value? use a pointer, or in Scheme, since no explicit pointers, use the following record: ... (define-record ae (array index)) Note: the same trick would be useful in Java *** changes to interpreter see $PUB/lib/ch6-2.scm for the whole thing This is different than the book. ------------------------------------------ CHANGES TO THE CALL-BY-VALUE INTERPRETER FOR CALL-BY-REFERENCE (INDIRECT MODEL) ;;; Figure 6.2.1 : page 191 (revised) (define eval-rand ;; TYPE: (-> (parsed-exp Environment) ;; Denoted-Value) (lambda (rand env) (variant-case rand (varref (var) (arrayref (array index) (define denoted->expressed ;; TYPE: (-> (Denoted-Value) ;; Expressed-Value) (lambda (den-val) (cond ((cell? den-val) (cell-ref den-val)) ((ae? den-val) (else (error "Can't dereference: " den-val))))) ------------------------------------------ ... (apply-env env var)) (make-ae (expressed->array (eval-array-exp array env)) (expressed->number (eval-exp index env)))) (else (expressed->denoted (eval-exp rand env)))))) ;; changed! Note that we use expressed->denoted instead of make-cell above. Q: How does eval-rand treat array names? as varrefs, so the array variable is passed by reference. Q: Is that different from how it treats expressions returning arrays? yes ... (array-ref (ae->array den-val) (ae->index den-val))) Q: can the error ever happen? no but this is defensive. ------------------------------------------ OTHER AUXILIARY FUNCTIONS FOR CALL-BY-REFERENCE (INDIRECT MODEL) (define denoted-value-assign! ;; TYPE: (-> (Denoted-Value ;; Expressed-Value) void) (lambda (den-val val) (cond ((cell? den-val) (cell-set! den-val val)) ((ae? den-val) (else (error "Can't assign to :" den-val))))) (define array-element-assign! ;; TYPE: (-> ((Array Expressed-Value) ;; number ;; Expressed-Value) ;; void) array-set!) ------------------------------------------ ... (array-element-assign! (ae->array den-val) (ae->index den-val) val)) Q: What about do-letarray, does it change from 6.1.3's interpreter? no Q: How about eval-array-exp? no ------------------------------------------ INDIRECT MODEL EXAMPLE letarray a[3]; b[2] in let p = proc(x,y) begin y := b; x := 5 end in begin a[0] := 1; a[1] := 2; a[2] := 3; b[0] := 4; b[1] := 6; p(a[1], a); list(a[0],a[1],b[0],b[1]) end ------------------------------------------ this returns (4 6 4 6) ** model with direct arrays *** motivation (differences) ------------------------------------------ DIRECT MODEL EXAMPLE letarray a[3]; b[2] in let p = proc(x,y) begin y := b; x := 5 end in begin a[0] := 1; a[1] := 2; a[2] := 3; b[0] := 4; b[1] := 6; p(a[1], a); list(a[0],a[1],b[0],b[1]) end ------------------------------------------ ------------------------------------------ INDIRECT VS. DIRECT MODEL letarray u[3]; v[2] in let p = proc (x) begin x := v; x[0] := 0 end in begin u[0] := 5; u[1] := 6; u[2] := 4; v[0] := 3; v[1] := 8; p(u); print(v[0]) end ------------------------------------------ Draw, or have them draw, what happens when this executes in the indirect or direct model? Q: What would print(u[0]) print at the end? *** model interpreter changes The following are homework problems: Q: What is meant by passing an array element by reference? Q: What would have to change in the interpreter? Look at domains (same as before, but): ------------------------------------------ CALL-BY-REFERENCE WITH DIRECT ARRAYS Domains: Environment = -> Denoted-Value Denoted-Value = Expressed-Value = Number + Procedure + Array(Storable-Value) Procedure = prim-proc + closure Storable-Value = Number + Procedure Array(T) = {Cell(T)}* ------------------------------------------ ... Cell(Storable-Value) + Array(Storable-Value) + Array-Element(Storable-Value)