meeting -*- Outline -*- * representation strategies for data types (2.3) ** motivation and overview corresponds to semantics, big picture for interpreters and compilers also ------------------------------------------ TRANSFORMING PROCEDURAL REPS TO ABSTRACT SYNTAX TREE REPS (2.3) Why? In semantics, often specify ADTs that are like functions: Idea: A. Represent each way to construct a value of such an ADT by a _________. To observe the stored information, apply them. B. If need efficiency, represent each way to construct a value by a __________ in a variant record type. To observe the stored information, use a cases expression, and the body of the corresponding ______________. ------------------------------------------ ...environment = var -> location store = location -> value array = nat -> location proc = values * store -> value * store ... closure ... variant ... closure ------------------------------------------ PICTURE OF TRANSFORMATION (nnnnnnnnnnnnnnnnnn) ( ) ( ADT ) ( ) (uuuuuuuuuuuuuuuuuu) ^ ^ / \ / \ / \ !---------------! !-----------------! ! procedural rep! ! AST rep ! ! ! **> !(variant records)! ! ! ! ! !---------------! !-----------------! ------------------------------------------ use this to give an overview, explain that each will implement the abstraction the transformation is the arrow from the procedural rep box to the record rep box The record rep is useful in normal and OO languages. ** procedural reps (2.3.2) ------------------------------------------ EXAMPLE: INFINITE SEQUENCES type seq with Constructors seq-repeat: (-> (number) seq) seq-generator: (-> ((-> (number) number)) seq) seq-add: (-> (seq seq) seq) Observers: seq-nth: (-> (seq number) number) Example: > (require (lib "seq-as-proc.scm" "lib342")) > (define ones (seq-repeat 1)) > (seq-nth ones 3) 1 > (define halves (seq-generator (lambda (n) (/ 1 (expt 2 n))))) > (seq-nth halves 0) 1 > (seq-nth halves 1) 0.5 > (seq-nth halves 2) 0.25 > (seq-nth halves 99) 1.577721810442e-30 > (seq-nth (seq-add ones halves) 2) 1.25 ------------------------------------------ Q: how would you represent seqs as closures? ------------------------------------------ INFINITE SEQUENCES AS PROCEDURES (module seq-as-proc (lib "typedscm.ss" "typedscm") (provide seq-repeat seq-generator seq-nth seq-add) (deftype seq-repeat (-> (number) seq)) (deftype seq-generator (-> ((-> (number) number)) seq)) (deftype seq-nth (-> (seq number) number)) (deftype seq-add (-> (seq seq) seq)) (defrep (seq (-> (number) number))) (define seq-repeat (lambda (num) (define seq-generator (lambda (f) (define seq-nth (lambda (s n) (define seq-add (lambda (s1 s2) ) ;; end module ------------------------------------------ ... (lambda (n) num))) ... (lambda (n) (f n)))) ... (s n))) ... (lambda (n) (+ (seq-nth s1 n) (seq-nth s2 n))))) Write out the concrete types, and draw the correspondence to the abstract types. Q: Which are the constructors? Q: How did we represent them? Q: Which are the observers? Q: How did we program them? Q: How is this like objects? -each seq object has one "method", which is how to get it's nth element -easy to add new client code, harder to add new methods ** abstract syntax tree representation (2.3.3) ------------------------------------------ TRANSFORMING TO ASTs (1) How? 1. Constructors: give a define-datatype with a variant record for each to remember its arguments (define seq-repeat ; procedural rep (lambda (num) (lambda (n) num))) (define seq-generator (lambda (f) (lambda (n) (f n)))) (define seq-add (lambda (s1 s2) (lambda (n) (+ (seq-nth s1 n) (seq-nth s2 n))))) ;;; ... becomes ... (define-datatype seq seq? ------------------------------------------ ... (seq-repeat (num number?)) (seq-generator (f (-> (number?) number?))) (seq-add (s1 seq?) (s2 seq?))) The easy thing to remember is just to use a field for each argument in each constructor. Technically, you need to have a field only for the free variables in the closure that aren't globally bound. Q: what are the free varrefs in the body of seq-repeat? seq-generator? Q: Can you add them all up at the time you make one of these kinds of sequences? ------------------------------------------ TRANSFORMING TO ASTS (2) How? 2. Use the code from each closure in the corresponding case of the observer cases expression (define seq-nth ; procedural version (lambda (s n) (s n))) (define seq-nth ; ast version (lambda (s n) (cases seq s ( ------------------------------------------ Q: What are the cases? (seq-repeat (num) num) (seq-generator (f) (f n)) (seq-add (s1 s2) (+ (seq-nth s1 n) (seq-nth s2 n)))))) Q: will this still work with our examples? Yes, provided we go back and redefine ones and halves... Both can't coexist in the program (unlike OOP) ** environment example ------------------------------------------ ENVIRONMENT SPECIFICATION def: An *environment* is a function that maps symbols to values. type environment with constructors: empty-env : (-> () environment) extend-env : (-> ((list-of symbol) (list-of datum) environment) environment) observer: apply-env : (-> (environment symbol) datum) BEHAVIOR (empty-env) = {} (extend-env (list 's1 ... 'sn) (list v1 ... vn) f) = (apply-env f s) = ------------------------------------------ ... {(s1,v1), ..., (sn,vn)} \union {(s,v) | (s,v) \in f, s \not\in {s1,...,sn}} ... v if (s,v) \in f Q: How would you represent these by procedures? ------------------------------------------ FOR YOU TO DO Transform the following to the AST rep. (module environment-as-proc (lib "typedscm.ss" "lib342") (provide empty-env extend-env apply-env) (deftype empty-env (-> () environment)) (deftype extend-env (-> ((list-of symbol) (list-of datum) environment) environment)) (deftype apply-env (-> (environment symbol) datum)) (require (lib "list-index.scm" "lib342")) (defrep (environment (-> (symbol) datum))) (define empty-env (lambda () (lambda (sym) (eopl:error 'apply-env "No binding for ~s" sym)))) (define extend-env (lambda (syms vals env) (lambda (sym) (let ((pos (list-index sym syms))) (if (<= 0 pos) (list-ref vals pos) (apply-env env sym)))))) (define apply-env (lambda (env sym) (env sym))) ) ;; end module ------------------------------------------ See $PUB/lib/environment-as-ast.scm and the book, p. 60 for the answer ** Relation to OO design patterns Like the Command design pattern. The basic idea of the Command pattern is to "encapsulate a request as an object" (GoF book inside cover) which is what the variants in our transformation do. Procedure arguments, like the argument to seq-generator, are best thought of as strategy patterns instances. ** alternatives (2.3.4), can skip ------------------------------------------ OPTIMIZATION ALTERNATIVES For convenience or efficiency consider other representations Environments built using grammar: ::= (empty-env) | (extend-env (list {}*) (list {}*) ) Can represent this directly: ::= () | ((({}*) ({}*)) . ) Observe that we look up the symbols, and get an index, so can use a vector for the values: ::= () | ((({}*) #({}*)) . ) Looks like: ((sym-list4 . val-vector4) (sym-list3 . val-vector3) ... (sym-list1 . val-vector1)) ------------------------------------------ Q: What serves as the tags to discriminate these options? draw picture as in Figure 2.4 Q: which is more convenient? depends