From isu.coms.227 Mon Oct 25 15:12:25 1993 Newsgroups: isu.coms.227 Path: news.iastate.edu!leavens From: leavens@cs.iastate.edu (Gary Leavens) Subject: Environments and letrec example Message-ID: Summary: Letrec values evaluated in its scope Keywords: environments, letrec Sender: news@news.iastate.edu (USENET News System) Organization: Iowa State University, Ames IA Distribution: isu Date: Fri, 22 Oct 1993 20:12:59 GMT Lines: 515 Here's an example of a LETREC done with environments, in response to a question by Charles (Chuck) Vanderdaan. Env0: cons ~~> [primitive-procedure|(x l)|...|Env0] (LETREC ([n-reverse-sublist (LAMBDA (n ls) (LET ([n-dup-on (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)))]) (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))))]) (helper ls))))]) (n-reverse-sublist 2 '((a b)))) To evaluate the LETREC, we set up a new environment Env1 (parent Env0): n-reverse-sublist ~~~> ?? and in this envrionment we evaluate the expression that will give the value of ?? (LAMBDA (n ls) (LET ([n-dup-on (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)))]) (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))))]) (helper ls)))) ==> [procedure |(n ls) |(LET ([n-dup-on (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)))]) (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))))]) (helper ls))) |Env1] so that Env1 looks like the following, in which we do the body of the LETREC Env1 (parent Env0): n-reverse-sublist ~~~> [procedure|(n ls)|(LET ...)|Env1] (n-reverse-sublist 2 '((a b))) To evaluate the application we evaluate the operator and args n-reverse-sublist ==> [procedure |(n ls) |(LET ([n-dup-on (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)))]) (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))))]) (helper ls))) |Env1] 2 ==> 2 '((a b)) ==> ((a b)) then evaluating the body of the closure in the following Env2 (parent Env1): n ~~~> [2] ls ~~> [((a b))] (LET ([n-dup-on (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)))]) (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))))]) (helper ls))) To evaluate the LET we need to compute the value of (LAMBDA (to-dup tail) (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n))) ==> [procedure |(to-dup tail) |(LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)) |Env2] Then we evaluate the body of the LET in the following Env3 (parent Env2): n-dup-on ~~> [procedure |(to-dup tail) |(LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)) |Env2] (LETREC ([helper (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls))))))) (helper ls)) To evaluate the LETREC, we set up a new environment Env4 (parent Env3): helper ~~~> ?? and in this environment we evaluate the expression that will give the value of ?? (LAMBDA (ls) (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls))))) ==> [procedure |(ls) |(IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) |Env4] so that Env4 looks like the following, in which we do the body of the LETREC Env4 (parent Env3): helper ~~~> [procedure |(ls) |(IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) |Env4] (helper ls) To evaluate the application we first eval the operator and args helper ==> [procedure |(ls) |(IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) |Env4] ls ==> ((a b)) then we evaluate the body of the closure (for helper) in... Env5 (parent Env4): ls ~~> [((a b))] (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) = (n-dup-on (reverse (car ls)) (helper (cdr ls))) To evaluate the application we first eval the operator and args n-dup-on ==> [procedure |(to-dup tail) |(LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)) |Env2] (reverse (car ls)) To evaluate the application we first eval the operator and args reverse ==> [primitive-procedure|(ls)|...|Env0] (car ls) ==> (a b) ==> (b a) (helper (cdr ls)) To evaluate the application we first eval the operator and args helper ==> [procedure |(ls) |(IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) |Env4] (cdr ls) ==> () then we evaluate the body of the closure (for helper) in Env6 (parent Env4): ls ~~~> [()] (IF (null? ls) '() (n-dup-on (reverse (car ls)) (helper (cdr ls)))) ==> () then we evaluate the body of the closure (for n-dup-on) in Env7 (parent Env2) ;; why is the parent Env2? to-dup ~~> [(b a)] tail ~~> [()] (LETREC ([n-dup (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))))]) (n-dup n)) To evaluate the LETREC, we set up a new environment Env8 (parent Env7): n-dup ~~~> ?? and in this environment we evaluate the expression that will give the value of ?? (LAMBDA (n*) (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*))))) ==> [procedure |(n*) |(IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) |Env8] so that Env8 looks like the following, in which we do the body of the LETREC Env8 (parent Env7): n-dup ~~~> [procedure |(n*) |(IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) |Env8] (n-dup n) To evaluate the application we first eval the operator and args n-dup ==> [procedure |(n*) |(IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) |Env8] n ==> 2 Then we evaluate the body of the closure (for n-dup) in Env9 (parent Env8): n* ~~> 2 (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) = (cons to-dup (n-dup (sub1 n*))) To evaluate the application we first eval the operator and args cons ==> [primitive-procedure|(x l)|...|Env0] to-dup ==> (b a) (n-dup (sub1 n*)) To evaluate this application we first eval the operator and args n-dup ==> [procedure |(n*) |(IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) |Env8] (sub1 n*) ==> 1 Then we evaluate the body (of the closure for n-dup) in Env10 (parent Env8): n* ~~> [1] (IF (zero? n*) tail (cons to-dup (n-dup (sub1 n*)))) ==> ((b a)) ==> ((b a) (b a)) The general rule for LETREC is thus as follows. The rule for evaluating a LETREC in an environment is that we first set up a new environment, whose parent is the original environment (Envk below), with bindings for all the formal parameters (to something unknown, ?? here). Then we evaluate the value expressions, which are typically lambdas, in that environment (not in the surrounding one). Note the environment stored in the closures! Then we bind the formal parameters to the corresponding closures, and evaluate the body in the resulting environment. Envk (parent Envj): y ~~> [v] (LETREC ((x1 (lambda (x11 ... x1m) e1)) ... (xn (lambda (xn1 ... xnm) en))) body) To evaluate the LETREC, we set up a new environment Env1 (parent Envk): x1 ~~~> ?? ... xn ~~~> ?? and in this envrionment we evaluate the expressions that will give the values of the formals to replace the ?? (lambda (x11 ... x1m) e1) ==> [procedure|(x11 ... x1m)|e1|Env1] ... (lambda (xn1 ... xnm) en) ==> [procedure|(xn1 ... xnm)|en|Env1] so that Env1 looks like the following, in which we do the body of the LETREC Env1 (parent Env0): x1 ~~~> [procedure|(x11 ... x1m)|e1|Env1] ... xn ~~~> [procedure|(xn1 ... xnm)|en|Env1] body ==> value Gary -- 229 Atanasoff Hall, Department of Computer Science Iowa State University, Ames, Iowa 50011-1040 USA/leavens@cs.iastate.edu phone: (515) 294-1580 fax: (515) 294-0258 ftp site: ftp.cs.iastate.edu