Com S 342 meeting -*- Outline -*- * Variable Assignment (3.7) Note: when drawing pictures of environments, use pictures like those needed for section 3.8 That is, draw it showing names labeling the cells empty-env ^ | init-env [ *-]---> [extended-env-record | * | * | * ] | | v v x [ *-]---> 3 y [ *-]---> 4 z [ *-]---> 2 empty-env or more schemematically: ^ | parent [ * ] init-env [ *-]----> x [ *-]---> 3 y [ *-]---> 4 z [ *-]---> 2 ** syntax ------------------------------------------ ASSIGNMENT (3.7) Examples: set x = 3 set y = +(y, x) Concrete syntax: ::= set = Abstract syntax: (define-datatype expression expression? ; ... ------------------------------------------ give another example! ... (varassign-exp (id symbol?) (rhs-exp expression?)) ** semantics Q: How would you describe an assignment? changes the value of a variable What's a variable, semantically? a box, object, cell Do we have those around? no *** domains ------------------------------------------ PROBLEM WITH ENVIRONMENTS Currently: Environment = symbol -> Denoted-Value Denoted Value = Expressed-Value Expressed-Value = Number + ProcVal Can an expression change the environment like assignment should? Example: let x = 5 in let addx = proc(n) +(n,x) in let first = proc(ignored) addx(20) in first(set x = 10) ------------------------------------------ ... it can extend it, but that's different than assignment, let extends the environment, but doesn't affect caller Q: How could we make that happen? use references ------------------------------------------ REFERENCES We add an operation to environments: (deftype apply-env-ref (-> (environment symbol) (ref-of Expressed-Value))) We use the following ADT (deftype deref (forall (T) (-> ((ref-of T)) T))) (deftype setref! (forall (T) (-> ((ref-of T) T) void))) Where, for all types T, if r is a (ref-of T), and ev, ev2 are values of type T ev = (begin (setref! r ev) (deref r)) ev = (begin (setref! r ev2) (setref! r ev) (deref r)) ------------------------------------------ ------------------------------------------ NEW DOMAINS Expressed-Value = Number + ProcVal Denoted-Value = Ref(Expressed-Value) Environment = symbol -> Denoted-Value ------------------------------------------ *** implementation **** reference ADT ------------------------------------------ IMPLEMENTATION OF THE REFERENCE ADT ;; file reference-3-7.scm (module reference-3-7 (lib "typedscm.ss" "lib342") (provide ref-of a-ref deref setref!) (define-datatype reference reference? (a-ref (position integer?) (vec vector?))) (deftype primitive-deref (forall (T) (-> ((ref-of T)) T))) (define primitive-deref (lambda (ref) (cases reference ref (a-ref (pos vec) (vector-ref vec pos))))) (deftype primitive-setref! (forall (T) (-> ((ref-of T) T) void))) (define primitive-setref! (lambda (ref val) (cases reference ref (a-ref (pos vec) (vector-set! vec pos val))))) (define deref (lambda (ref) (primitive-deref ref))) (define setref! (lambda (ref val) (primitive-setref! ref val))) ) ;; end module ------------------------------------------ Now we have to add apply-env-ref (and change apply-env) **** environment ADT ------------------------------------------ CHANGES TO ENVIRONMENT IMPLEMENTATION (define-datatype environment environment? (empty-env-record) (extended-env-record (syms (list-of symbol?)) (vec vector?) (env environment?)) ) ; ... (define extend-env (lambda (syms vals env) (extended-env-record syms (list->vector vals) env))) (define apply-env-ref (lambda (env sym) (cases environment env (empty-env-record () (eopl:error 'apply-env-ref "No binding for ~s" sym)) (extended-env-record (syms vals env) (let ((pos (list-index sym syms))) (if (<= 0 pos) (a-ref pos vals) ; ** (apply-env-ref env sym))))))) (define apply-env (lambda (env sym) (deref (apply-env-ref env sym)))) ------------------------------------------ Note that the change to apply-env makes it still have the same type and behavior as before. So our new environment type is a refinement of the old one. *** interpreter ------------------------------------------ THE INTERPRETER WITH ASSIGNMENT What changes to the interpreter? ;;; Figure 3.15, p. 103 (define eval-expression (lambda (exp env) (cases expression exp (var-exp (id) (apply-env env id)) ;; ... ------------------------------------------ ... (varassign-exp (id rhs-exp) (begin (setref! (apply-env-ref env id) (eval-expression rhs-exp env)) (number->expressed 1))) *** Changes to init-env Q: Are any changes needed in init-env? No, extend-env implements the same interface, and takes care of allocating the mutable locations itself. ** semantic implications and variations *** result Q: What is the value returned by an assignment expression? 1 Q: What other options are there? the right hand side value, how would we implement that? *** sequencing (exercise 3.39) Q: Once we have sequencing and assignment, what other syntax could we have in the defined language? - for, while, etc., see section 3.9 show how begin was added to the interpreter (it's there already in ch3-7.scm) *** references and call-by-value A storage location (L-value) is what a reference points to. Q: How are storage locations represented in the interpreter? as elements of vectors in the ribcage rep of environments ------------------------------------------ CALL-BY-VALUE Example: let x = 100 in let p = proc(y) let d = set y = add1(y) in y in +((p x), (p x)) What does this return? Example: let x = 100 in let p = proc(y) let d = set x = add1(y) in x in +((p x), (p x)) What does this return? ------------------------------------------ ... 202 Draw picture of how these execute Q: Where does the location that set y = add1(y) come from? the formal ... 203 Q: Why are these results different? scoping Q: Where does the location that set x = add1(y) come from? the let Q: So When are the storage locations created? When we extend an environment: i.e., in let, and applying procedures This is call-by-value we allocate a location for each formal parameter, and initialize it with the actual parameter. *** explicit allocation, dereferencing (as in BCPL, exercise 3.41) (skip) Q: What happens if we make location expressed values, with operations cell, contents, and setcell? Then assignment is a primitive (setcell) not a special form. Expressed-Value = Number + ProcVal + Ref(Expressed-Value) Denoted-Value = Expressed-Value ------------------------------------------ let g = let count = cell(0) in proc() begin setcell( count, add1(contents(count))) contents(count) end in +((g), (g)) ------------------------------------------ Q: How would you do something like the C/C++ address-of (&) operator? As in Exercise 3.43, have a "ref" special form ::= ref And add primitives deref (like the C/C++ special form "*") and a special form setref (or a special form...) *** arrays (exercise 3.42) (homework problem) Q: What primitives would you add to add arrays to the language? Arr = (Ref(Expressed-Value))* Expressed-Value = Number + ProcVal + Arr Denoted-Value = Ref(Expressed-Value) *** constant bindings (exercise 3.45) (skipped) Q: How would you implement something like C++ "const" variables or Java "final" variables? Expressed-Value = Number + ProcVal Denoted-Value = Ref(Expressed-Value) + Expressed-Value Have to have two kinds of let, and perhaps 2 kinds of formal in procedures, to make mutable and immutable bindings. Have to change environment abstraction. How? *** dynamic assignment or fluid binding (exercise 3.47) Temporarily assign to a variable during execution of an expression ::= setdynamic = during meaning of setdynamic x = e0 during e1 is let oldx = x temp = 0 in begin set x = e0 temp = e1 set x = oldx temp end *** semantic ideas (exercise 3.48) (skip) model store explicitly, and pass it around