6.037 Lecture 7B Scheme Variants Normal Order Lazy Evaluation Streams Edited by Mike Phillips & Ben Vandiver Original Material by Eric Grimson & Duane.

Slides:



Advertisements
Similar presentations
1 Programming Languages (CS 550) Lecture Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
Advertisements

1 The metacircular evaluator Names Extend the calculator to store intermediate results as named values (define x (+ 4 5)) store result as x (+ x.
1 Programming Languages (CS 550) Operational Semantics of Scheme using Substitution and the Lambda Calculus Jeremy R. Johnson TexPoint fonts used in EMF.
Metacircular Evaluation SICP Chapter 4 Mark Boady.
1 Programming Languages (CS 550) Lecture 7 Summary Operational Semantics of Scheme using Substitution Jeremy R. Johnson TexPoint fonts used in EMF. Read.
מבוא מורחב למדעי המחשב תרגול 13 Lazy Evaluation. Lazy Evaluation Implementation Two types of values – Delayed Values – Actual Values Introduce two functions.
Cs7100(Prasad)L11Clos1 Closures and Streams. Contemporary Interest in Closures The concept of closures was developed in the 1960s and was first fully.
1 The Metacircular Evaluator Chapter 4 Section 4.1 We will not cover every little detail in the lectures, so you MUST read Section 4.1 in full.
מבוא מורחב - שיעור 15 1 Lecture 15 Streams. מבוא מורחב - שיעור 15 2 Streams: Motivation (define (sum-primes a b) (define (iter count accum) (cond ((>
1 Lecture 18 Continue Evaluator. 2 z9 true#t + twice Representing procedures (eval '(define twice (lambda (x) (+ x x))) GE) symbol primitive scheme procedure.
6.001 SICP 1 Normal (Lazy) Order Evaluation Memoization Streams.
Extended Introduction to CS Preparation for Final Exam.
David Evans CS200: Computer Science University of Virginia Computer Science Lecture 26: In Praise of Idleness.
Functional programming: LISP Originally developed for symbolic computing Main motivation: include recursion (see McCarthy biographical excerpt on web site).
מבוא מורחב למדעי המחשב בשפת Scheme תרגול 21. Overview 1. Dynamic Binding 2. Lazy Evaluation 3. More MC-Eval 2.
Lexical vs. Dynamic Scope …where do I point to?. Intro… Remember in Scheme whenever we call a procedure we pop a frame and point it to where the procedure.
SICP Variations on a Scheme Scheme Evaluator – A Grand Tour Techniques for language design: Interpretation: eval/apply Semantics vs. syntax Syntactic.
SICP Infinite streams – using lazy evaluation Beyond Scheme – designing language variants: Streams – an alternative programming style!
Functional programming: LISP Originally developed for symbolic computing First interactive, interpreted language Dynamic typing: values have types, variables.
Scheme More MCE examples. Q1 new special form which defines global variables (static ) search the global environment – Variable exists: does nothing,
SICP Variations on a Scheme (2) Beyond Scheme – designing language variants: Lazy evaluation Complete conversion – normal order evaluator Upward.
Closures and Streams More on Evaluations CS784(pm)1.
1 Lecture OO, the notion of inheritance 3 Stacks in OO style (define (make-stack) (let ((top-ptr '())) (define (empty?) (null? top-ptr)) (define.
1 Continue Evaluator. 2 z9 true#t + twice Representing procedures (eval '(define twice (lambda (x) (+ x x))) GE) symbol primitive scheme procedure + symbol.
1 The metacircular evaluator (Cont.) Defining new procedures (define (lambda? e) (tag-check e 'lambda)) (define (eval exp env) (cond ((number? exp)
CS220 Programming Principles 프로그래밍의 이해 2002 가을학기 Class 15: Meta-Circular Evaluator 한 태숙.
SICP Explicit-control evaluator Big ideas: how to connect evaluator to machine instructions how to achieve tail recursion Obfuscation: tightly.
1 Lecture 20 Lazy Evaluation Continued (4.2.1, 4.2.2) MC-eval examples from exams (Time permitting)
1 Read-Eval-Print Loop (define (driver-loop) (prompt-for-input input-prompt) (let ((input (read))) (let ((output (eval input the-global-env))) (announce-output.
Basic Scheme February 8, 2007 Compound expressions Rules of evaluation Creating procedures by capturing common patterns.
1/33 Basic Scheme February 8, 2007 Compound expressions Rules of evaluation Creating procedures by capturing common patterns.
1 Programming Languages (CS 550) Lecture 4 Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
1 Lecture 19 Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)
1/ SICP Variations on a Scheme Scheme Evaluator – A Grand Tour Making the environment model concrete Defining eval defines the language –Provides.
1 Lecture 19 Review last lecture on evaluator, Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)
1 The Evaluator. 2 Compiler vs. Interpreter Command Processing Unit The Computer Program in Low Level Machine Language Program in High Level Language.
CSE 341 Lecture 21 delayed evaluation; thunks; streams slides created by Marty Stepp
(Thunking about Thunks)
Operational Semantics of Scheme
Basic Scheme February 8, 2007 Compound expressions Rules of evaluation
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2017.
6.001 SICP Variations on a Scheme
September 4, 1997 Programming Languages (CS 550) Lecture 6 Summary Operational Semantics of Scheme using Substitution Jeremy R. Johnson TexPoint fonts.
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Winter 2013.
Env. Model Implementation
Original material by Eric Grimson
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Zach Tatlock Winter 2018.
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2013.
Streams Sections 3.5.1,3.5.2 Pages
The Metacircular Evaluator
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2016.
Dynamic Scoping Lazy Evaluation
The Metacircular Evaluator
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2017.
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2018.
6.001 SICP Streams – the lazy way
The Metacircular Evaluator (Continued)
Lecture 27: In Praise of Idleness CS200: Computer Science
Lecture 26: The Metacircular Evaluator Eval Apply
6.001 SICP Further Variations on a Scheme
Streams, Delayed Evaluation and a Normal Order Interpreter
6.001 SICP Variations on a Scheme
6.001 SICP Interpretation Parts of an interpreter
Introduction to the Lab
topics interpreters meta-linguistic abstraction eval and apply
Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator
Brett Wortzman Summer 2019 Slides originally created by Dan Grossman
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2019.
Lecture 25: The Metacircular Evaluator Eval Apply
Presentation transcript:

6.037 Lecture 7B Scheme Variants Normal Order Lazy Evaluation Streams Edited by Mike Phillips & Ben Vandiver Original Material by Eric Grimson & Duane Boning

2/31 Further Variations on a Scheme Beyond Scheme – more language variants Lazy evaluation – Complete conversion – normal order evaluator – Selective Laziness: Streams Punchline: Small edits to the interpreter give us a new programming language

3/31 Environment model Rules of evaluation: If expression is self-evaluating (e.g. a number), just return value If expression is a name, look up value associated with that name in environment If expression is a lambda, create procedure and return If expression is special form (e.g. if) follow specific rules for evaluating subexpressions If expression is a compound expression – Evaluate subexpressions in any order – If first subexpression is primitive (or built-in) procedure, just apply it to values of other subexpressions – If first subexpression is compound procedure (created by lambda), evaluate the body of the procedure in a new environment, which extends the environment of the procedure with a new frame in which the procedure’s parameters are bound to the supplied arguments

4/31 Alternative models for computation Applicative Order (aka Eager evaluation): – evaluate all arguments, then apply operator Normal Order (aka Lazy evaluation: – go ahead and apply operator with unevaluated argument subexpressions – evaluate a subexpression only when value is needed to print by primitive procedure (that is, primitive procedures are "strict" in their arguments) to test (if predicate) to apply (operator)

Making Order of Evaluation Visible (define (notice x) (display “noticed”) x) (+ (notice 52) (notice (+ 4 4)) noticed noticed => 60

6/31 Applicative Order Example (define (foo x) (display "inside foo") (+ x x)) (foo (notice 222)) We first evaluated argument, then substituted value into the body of the procedure noticed => (begin (display "inside foo") ( )) => 222 => (notice 222) => 444 inside foo

7/31 Normal Order Example (define (foo x) (display "inside foo") (+ x x)) (foo (notice 222)) As if we substituted the unevaluated expression in the body of the procedure => (begin (display "inside foo") (+ (notice 222) (notice 222))) inside foo noticed => 444 From body of foo

8/31 Applicative Order vs. Normal Order (define (foo x) (display "inside foo") (+ x x)) (foo (notice 222)) inside foo noticed Normal order noticed inside foo Applicative order Think of as expanding expressions until only involve primitive operations and data structures Think of as substituting values for variables in expressions

9/31 Normal order (lazy evaluation) versus applicative order How can we change our evaluator to use normal order? – Create “promises” – expressions whose evaluation has been delayed – Change the evaluator to force evaluation only when needed Why is normal order useful? – What kinds of computations does it make easier?

10/31 m-apply – the original version (define (m-apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-environment procedure)))) (else (error "Unknown procedure" procedure)))) Actual values

11/31 How can we implement lazy evaluation? (define (l-apply procedure arguments env) ; changed (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure (list-of-arg-values arguments env))) ((compound-procedure? procedure) (l-eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) (list-of-delayed-args arguments env) (procedure-environment procedure)))) (else (error "Unknown proc" procedure)))) Delayed expressions Delayed Expressions Need to convert to actual values Need to create delayed version of arguments that will lead to values

12/31 Lazy Evaluation – l-eval Most of the work is in l-apply ; need to call it with: – actual value for the operator – just expressions for the operands – the environment... (define (l-eval exp env) (cond ((self-evaluating? exp) exp)... ((application? exp (l-apply (actual-value (operator exp) env) (operands exp) env)) (else (error "Unknown expression" exp))))

13/31 m-eval versus l-Eval (define (m-eval exp env) (cond ((self-evaluating? exp) exp) … ((cond? exp) (m-eval (cond->if exp) env)) ((application? exp) (m-apply (m-eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type -- EVAL" exp)))) (define (l-eval exp env) (cond ((self-evaluating? exp) exp)... ((cond? exp) ((application? exp (l-apply (actual-value (operator exp) env) (operands exp) env)) (else (error "Unknown expression" exp))))

14/31 Actual vs. Delayed Values (define (actual-value exp env) (force-it (l-eval exp env))) (define (list-of-delayed-args exps env) (if (no-operands? exps) '() (cons (delay-it (first-operand exps) env) (list-of-delayed-args (rest-operands exps) env)))) (define (list-of-arg-values exps env) (if (no-operands? exps) '() (cons (actual-value (first-operand exps) env) (list-of-arg-values (rest-operands exps) env)))) Used when applying a primitive procedure Used when applying a compound procedure

15/31 Representing Promises Abstractly –a "promise" to return a value when later needed ("forced") Concretely – our representation: Book calls it a thunk, which means procedure with no arguments. Structure looks very similar. promise envexp

16/31 Promises – delay-it and force-it (define (delay-it exp env) (list ' promise exp env)) (define (promise? obj) (tagged-list? obj ' promise)) (define (promise-exp promise) (cadr promise)) (define (promise-env promise) (caddr promise)) (define (force-it obj) (cond ((promise? obj) (actual-value (promise-exp obj) (promise-env obj))) (else obj))) (define (actual-value exp env) (force-it (l-eval exp env)))

17/31 Lazy Evaluation – other changes needed Example: Need actual predicate value in conditional if... (define (l-eval-if exp env) (if (true? (actual-value (if-predicate exp) env)) (l-eval (if-consequent exp) env) (l-eval (if-alternative exp) env))) Example: Don't need actual value in assignment... (define (l-eval-assignment exp env) (set-variable-value! (assignment-variable exp) (l-eval (assignment-value exp) env) env) 'ok)

Examples (define identity (lambda (x) x)) (define a (notice 3)) (define b (identity (notice 3))) (define c b) (define d (+ b c)) (define plus (identity +)) (plus a b) c a: promise 3 Noticed! b: promise (notice 3) c: d: 6 Noticed! Noticed! plus: promise + => 6 Noticed! => 3 Noticed! identity :

19/31 Memo-izing evaluation In lazy evaluation, if we reuse an argument, have to reevaluate each time In usual (applicative) evaluation, argument is evaluated once, and just referenced Can we keep track of values once we’ve obtained them, and avoid cost of re- evaluation?

20/31 Memo-izing Promises Idea: once promise exp has been evaluated, remember it If value is needed again, just return it rather than recompute promise envexp Concretely – mutate a promise into an evaluated- promise evaluated- promise result Why mutuate? – because other names or data structures may point to this promise!

21/31 Promises – Memoizing Implementation (define (evaluated-promise? obj) (tagged-list? obj 'evaluated-promise)) (define (promise-value evaluated-promise) (cadr evaluated-promise)) (define (force-it obj) (cond ((promise? obj) (let ((result (actual-value (promise-exp obj) (promise-env obj)))) (set-car! obj 'evaluated-promise) (set-car! (cdr obj) result) (set-cdr! (cdr obj) '()) result)) ((evaluated-promise? obj) (promise-value obj)) (else obj)))

Examples - Memoized (define identity (lambda (x) x)) (define a (notice 3)) (define b (identity (notice 3))) (define c b) (define d (+ b c)) (define plus (identity +)) (plus a b) c a: promise 3 Noticed! b: promise (notice 3) c: d: 6 Noticed! *CHANGE* plus: promise + => 6 *CHANGE* => 3 *CHANGE* identity:

23/31 Summary of lazy evaluation This completes changes to evaluator – Apply takes a set of expressions for arguments and an environment Forces evaluation of arguments for primitive procedure application Else defers evaluation and unwinds computation further Need to pass in environment since don’t know when it will be needed – Need to force evaluation on branching operations (e.g. if) – Otherwise small number of changes make big change in behavior of language

24/31 Laziness and Language Design We have a dilemma with lazy evaluation – Advantage: only do work when value actually needed – Disadvantages not sure when expression will be evaluated; can be very big issue in a language with side effects may evaluate same expression more than once Memoization doesn't fully resolve our dilemma Advantage: Evaluate expression at most once Disadvantage: What if we want evaluation on each use? Alternative approach: Selective Laziness

25 Streams – the lazy way Beyond Scheme – designing language variants: Streams – an alternative programming style to infinity, and beyond…

26 Decoupling computation from description Can separate order of events in computer from apparent order of events in procedure description (list-ref (filter (lambda (x) (prime? x)) (enumerate-interval )) 100) Creates 1M elements Creates 100K elements Generate only what you actually need…

27 Stream Object A pair-like object, except the cdr part is lazy (not evaluated until needed): a promise a value stream-car cons-stream stream-cdr Example (define x (cons-stream 99 (/ 1 0))) (stream-car x) => 99 (stream-cdr x) => error – divide by zero Stream-cdr forces the promise wrapped around (/ 1 0), resulting in an error

28 Implementing Streams Stream is a data structure with the following contract: – (cons-stream a b) – cons together a with promise to compute b – (stream-car s) – Returns car of s – (stream-cdr s) – Forces and returns value of cdr of s Implement in regular evaluator with a little syntactic sugar – (define (cons-stream->cons exp) `(cons,(second exp) (lambda (),(third exp)))) – In m-eval, add to cond: ((cons-stream? exp) (m-eval (cons-stream->cons exp) env)) – And the following regular definitions (inside m-eval!) (define stream-car car) (define (stream-cdr s) ((cdr s))) Streams can be done in lazy eval – (define (cons-stream a b) (cons a b))  doesn’t work! (Why?)

Ints-starting-with (define (ints-starting-with i) (cons-stream i (ints-starting-with (+ i 1)))) Recursive procedure with no base case! – Why does it work? Delayed!

Stream-ref (define (stream-ref s i) (if (= i 0) (stream-car s) (stream-ref (stream-cdr s) (- i 1)))) Like list-ref, but cdr’s down stream, forcing

31 Stream-filter (define (stream-filter pred str) (if (pred (stream-car str)) (cons-stream (stream-car str) (stream-filter pred (stream-cdr str))) (stream-filter pred (stream-cdr str))))

32 Decoupling Order of Evaluation (define (stream-filter pred str) (if (pred (stream-car str)) (cons-stream (stream-car str) (stream-filter pred (stream-cdr str))) (stream-filter pred (stream-cdr str)))) (stream-ref (stream-filter (lambda (x) (prime? x)) (ints-starting-with 2)) 4)

33 Decoupling Order of Evaluation (stream-ref (stream-filter (lambda (x) (prime? x)) (ints-starting-with 2)) 4) Creates 1 element, plus a promise

34 Decoupling Order of Evaluation (stream-filter prime? (ints-from 1)) (ints- from 2) 1 (stream-filter prime? ) (ints- from 2) (stream-filter prime? ) (ints- from 3) 2 (stream-filter prime? (stream-cdr 2 From recursive call

35 One Possibility: Infinite Data Structures! Some very interesting behavior (define ones (cons 1 ones)) (define ones (cons-stream 1 ones)) (stream-car (stream-cdr ones)) => 1 1 ones The infinite stream of 1's! ones: (stream-ref ones 1)  1 (stream-ref ones 1000)  1 (stream-ref ones )  1

36 Finite list procs turn into infinite stream procs (define (add-streams s1 s2) (cons-stream (+ (stream-car s1) (stream-car s2)) (add-streams (stream-cdr s1) (stream-cdr s2)))))) (define ints (cons-stream 1 (add-streams ones ints))) ones: add-streams (str-cdr ones) (str-cdr ints) 3... add-streams ones ints 2ints: 1

37 Finding all the primes

38 Using a sieve (define (sieve str) (cons-stream (stream-car str) (sieve (stream-filter (lambda (x) (not (divisible? x (stream-car str)))) (stream-cdr str))))) (define primes (sieve (stream-cdr ints))) ( 2 sieve (filter ints 2) ) (2 3 sieve (filter sieve (filter ints 2) 3))

Interleave Produce a stream that has all the elements of two input streams: (define (interleave s1 s2) (cons-stream (stream-car s1) (interleave s2 (stream-cdr s1))))

Rationals 1/11/21/31/41/5… 2/12/22/32/42/5… 3/13/23/33/43/5… 4/14/24/34/44/5… 5/15/25/35/45/5… ……………… (define (div-by-stream s n) (cons-stream (/ n (stream-car s)) (div-by-stream (stream-cdr s) n)))) (define (make-rats n) (cons-stream n (interleave (div-by-streams (stream-cdr ints) n) (make-rats (+ n 1))))) (define rats (make-rats 1))

Power Series Approximate function by summation of infinite polynomial Great application for streams!