Bindings & Substitution

Similar documents
Functions & First Class Function Values

Environments

An Introduction to Functions

Evaluating Scheme Expressions

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

CS 360 Programming Languages Interpreters

CMSC 330: Organization of Programming Languages

Lecture 2: Big-Step Semantics

News. Programming Languages. Recap. Recap: Environments. Functions. of functions: Closures. CSE 130 : Fall Lecture 5: Functions and Datatypes

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

CS558 Programming Languages

Name EID. (calc (parse '{+ {with {x {+ 5 5}} {with {y {- x 3}} {+ y y} } } z } ) )

CS558 Programming Languages

CSE341 Spring 2017, Final Examination June 8, 2017

Higher-Order Functions (Part I)

CS558 Programming Languages

Project 2: Scheme Interpreter

Lecture08: Scope and Lexical Address

Typical workflow. CSE341: Programming Languages. Lecture 17 Implementing Languages Including Closures. Reality more complicated

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

Formal Semantics. Chapter Twenty-Three Modern Programming Languages, 2nd ed. 1

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

The Typed Racket Guide

CSE 413 Midterm, May 6, 2011 Sample Solution Page 1 of 8

Programming Languages

Variables. Substitution

CSE341 Spring 2017, Final Examination June 8, 2017

EECS 700 Functional Programming

Concepts of programming languages

Lecture 2: SML Basics

A simple syntax-directed

6.001 Notes: Section 15.1

Functional Programming. Pure Functional Languages

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda

Functional abstraction

Calculating Correct Compilers

Principles of Programming Languages

COP4020 Programming Assignment 1 - Spring 2011

Recap: Functions as first-class values

CSE341 Spring 2016, Final Examination June 6, 2016

Introduction to lambda calculus Part 3

Higher-Order Functions

CSE 341, Spring 2011, Final Examination 9 June Please do not turn the page until everyone is ready.

CMSC 330: Organization of Programming Languages. Formal Semantics of a Prog. Lang. Specifying Syntax, Semantics

Programming Languages. Dr. Philip Cannata 1

Scheme as implemented by Racket

Typed Racket: Racket with Static Types

CS 320 Homework One Due 2/5 11:59pm

Context-Free Grammar (CFG)

Random Testing in 321

Understanding Recursion

Functional Programming. Pure Functional Languages

Parser Tools: lex and yacc-style Parsing

Scope and Parameter Passing 1 / 19

Lecture 09: Data Abstraction ++ Parsing is the process of translating a sequence of characters (a string) into an abstract syntax tree.

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

Compilers. Type checking. Yannis Smaragdakis, U. Athens (original slides by Sam

CSE 341 Section 7. Ethan Shea Autumn Adapted from slides by Nicholas Shahan, Dan Grossman, Tam Dang, and Eric Mullen

The syntax and semantics of Beginning Student

The syntax and semantics of Beginning Student

Homework. Lecture 7: Parsers & Lambda Calculus. Rewrite Grammar. Problems

cs173: Programming Languages Midterm Exam

Interpreters. Prof. Clarkson Fall Today s music: Step by Step by New Kids on the Block

Prof. Mohamed Hamada Software Engineering Lab. The University of Aizu Japan

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

CSE 341 Section 7. Eric Mullen Spring Adapted from slides by Nicholas Shahan, Dan Grossman, and Tam Dang

Comp 311: Sample Midterm Examination

Principles of Programming Languages

mk-convert Contents 1 Converting to minikanren, quasimatically. 08 July 2014

The Environment Model. Nate Foster Spring 2018

CSE341, Spring 2013, Final Examination June 13, 2013

Title. Authors. Status. 1. Abstract. 2. Rationale. 3. Specification. R6RS Syntax-Case Macros. Kent Dybvig

Racket: Macros. Advanced Functional Programming. Jean-Noël Monette. November 2013

Chapter 3: Describing Syntax and Semantics. Introduction Formal methods of describing syntax (BNF)

SCHEME INTERPRETER GUIDE 4

Tracing Ambiguity in GADT Type Inference

1 Lexical Considerations

Defining syntax using CFGs

SCHEME AND CALCULATOR 5b

A Small Interpreted Language

Typing Control. Chapter Conditionals

Syntax. A. Bellaachia Page: 1

Module 8: Local and functional abstraction

Ways to implement a language

Principles of Programming Languages 2017W, Functional Programming

COSC252: Programming Languages: Semantic Specification. Jeremy Bolton, PhD Adjunct Professor

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Lecture 13 CIS 341: COMPILERS

LECTURE 16. Functional Programming

Recap. Recap. If-then-else expressions. If-then-else expressions. If-then-else expressions. If-then-else expressions

COP4020 Programming Assignment 2 - Fall 2016

Functional Programming

TAIL RECURSION, SCOPE, AND PROJECT 4 11

Programming Languages

Local definitions and lexical scope

Local definitions and lexical scope. Local definitions. Motivating local definitions. s(s a)(s b)(s c), where s = (a + b + c)/2.

Begin at the beginning

CSC324 Functional Programming Efficiency Issues, Parameter Lists

The Environment Model

Transcription:

Bindings & Substitution Even in our simple language, we encounter repeated expressions. 1 {* {+ 4 2} {+ 4 2}} There are several reasons to eliminate duplicated code.

It introduces a redundant computation. Repetition obscures meaning. 2 with x = {+ 4 2}, {* x x} Duplicating information is always a bad thing. Cut and paste leads to bugs: 3 {* {+ 4 2} {+ 4 1}}

Real world examples involve much more code, which make such bugs very difficult to find Binding gives us more expressive power it allows us to express identity of two values as well as using two values that happen to be the same. The normal way to avoid redundancy is to introduce an identifier. To get this, we introduce a new form into our language: 4 { with {x {+ 4 2}} {* x x}}

Real world examples involve much more code, which make 2017-03-02 such bugs very difficult to find Binding gives us more expressive power it allows us to express identity of two values as well as using two values that happen to be the same. The normal way to avoid redundancy is to introduce an identifier. To get this, we introduce a new form into our language: 4 { with {x {+ 4 2}} {* x x}} 1. These are often called variables, but we will try to avoid this name: what if the identifier does not change (vary)?

We can reduce this to: {* 6 6} by substituting 6 for x in the body sub-expression of with.

A little more complicated example: 5 { with {x {+ 4 2}} { with {y {* x x}} {+ y y }}} We can do a series of substitutions 6 [ add ] = { with {x 6} { with {y {* x x}} {+ y y }}} [ subst ]= { with {y {* 6 6}} {+ y y}} [ mul ] = { with {y 36} {+ y y}} [ subst ]= {+ 36 36} [ add ] = 72

Adding Bindings to AE: The WAE Language PLAI (1st ed) Chapter 3. PLAI (2nd ed) Section 5.3. To add with to our language, we start with the BNF. We now call our language WAE (With+AE): Two new rules: one for introducing an identifier, and one for using it. 7 wae: NUMBER { + wae wae } { - wae wae } { * wae wae } { / wae wae } { with { ID wae } wae } ID

For ID we need to use some form of identifiers, the natural choice in Racket is to use symbols. We can therefore write the corresponding type definition: 8 ( define- type WAE [ Num ( val : number )] [ Add (l : WAE ) (r : WAE )] [ Sub (l : WAE ) (r : WAE )] [ Mul (l : WAE ) (r : WAE )] [ Div (l : WAE ) (r : WAE )] [Id ( name : symbol )] [ With ( name : symbol ) ( val : WAE ) ( expr : WAE )])

For ID we need to use some form of identifiers, the natural 2017-03-02 choice in Racket is to use symbols. We can therefore write the corresponding type definition: 8 ( define-type WAE [ Num ( val : number )] [ Add (l : WAE ) (r : WAE )] [ Sub (l : WAE ) (r : WAE )] [ Mul (l : WAE ) (r : WAE )] [ Div (l : WAE ) (r : WAE )] [Id ( name : symbol )] [ With ( name : symbol ) ( val : WAE ) ( expr : WAE )]) 1. This is common in many language specifications, for example define-type introduces a new type, and it comes with cases that allows us to destruct its instances.

We can add another case (actually two) to our parser to handle the with form 9 ( cond. [( s-exp-symbol? sx) (Id ( s-exp- > symbol sx))] [( s-exp-match? '( with ( SYMBOL ANY ) ANY ) sx) ( let* ([ def ( sx-ref sx 1)] [id ( s-exp- > symbol ( sx-ref def 0))] [ val ( parse-sx ( sx-ref def 1))] [ expr ( parse-sx ( sx-ref sx 2))]) ( With id val expr ))]

Error handling could definitely be improved: 10 ( test/exn ( parse-sx '{* 1 2 3}) " parse error ") ( test/exn ( parse-sx '{ foo 5 6}) " parse error ") ( test/exn ( parse-sx '{ with x 5 {* x 8}}) " parse error ") ( test/exn ( parse-sx '{ with {5 x} {* x 8}}) " parse error ") ( test ( parse-sx '{ with {x 5} {* x 8}}) ( With 'x ( Num 5) ( Mul (Id 'x) ( Num 8))))

Implementing with Evaluation We will need to do some substitutions. To evaluate: 11 { with {id WAE1 } WAE2 } we need to evaluate WAE2 with id substituted by WAE1. Formally: 12 eval ( { with { id WAE1 } WAE2 } ) = eval ( subst (WAE2,id, WAE1 ) )

There is a more common syntax for substitution 13 eval ( { with { id WAE1 } WAE2 } ) = eval ( WAE2 [ WAE1/id ] ) Now all we need is an exact definition of substitution. Let us try to define substitution now: [substitution, take 1] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all identifiers in e that have the same name i by the expression v. This seems to work with simple expressions, for example: 14 { with {x 5} {+ x x}} -- > {+ 5 5} { with {x 5} {+ 10 4}} -- > {+ 10 4}

There is a more common syntax for substitution 2017-03-02 13 eval ( { with {id WAE1 } WAE2 } ) = eval ( WAE2 [ WAE1/id ] ) Now all we need is an exact definition of substitution. Let us try to define substitution now: [substitution, take 1] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all identifiers in e that have the same name i by the expression v. This seems to work with simple expressions, for example: { with {x 5} {+ x x}} -- > {+ 5 5} 14 { with {x 5} {+ 10 4}} -- > {+ 10 4} 1. Note that substitution is not the same as evaluation, only part of the evaluation process. In the previous examples, when we evaluated the expression we did substitutions as well as the usual arithmetic operations that were already part of the AE evaluator. In this last definition there is still a missing evaluation step, see if you can find it.

however, we crash with an invalid syntax if we try: 15 { with {x 5} {+ x { with {x 3} 10}}} -- > {+ 5 { with {5 3} 10}}??? we got to an invalid expression. To fix this, we need to distinguish normal occurrences of identifiers, and ones that are used as new bindings.

Binding Instance: a binding instance of an identifier is one that is used to name it in a new binding. In our BNF syntax, binding instances are only the ID position of the with form. Scope the scope of a binding instance is the region of program text in which instances of the identifier refer to the value bound in the binding instance. Bound Instance (or Bound Occurrence): an instance of an identifier is bound if it is contained within the scope of a binding instance of its name. Free Instance (or Free Occurrence): An identifier that is not contained in any binding instance of its name is said to be free.

Binding Instance: a binding instance of an identifier is one 2017-03-02 that is used to name it in a new binding. In our BNF syntax, binding instances are only the ID position of the with form. Scope the scope of a binding instance is the region of program text in which instances of the identifier refer to the value bound in the binding instance. Bound Instance (or Bound Occurrence): an instance of an identifier is bound if it is contained within the scope of a binding instance of its name. Free Instance (or Free Occurrence): An identifier that is not contained in any binding instance of its name is said to be free. 1. Note that this definition of scope actually relies on a definition of substitution, because that is what is used to specify how identifiers refer to values.

the previous definition of substitution failed to distinguish between bound instances and binding instances. So we try to fix this: [substitution, take 2] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all instances of i that are not themselves binding instances with the expression v. The previous working examples still work 16 { with {x 5} {+ x x}} -- > {+ 5 5} { with {x 5} {+ 10 4}} -- > {+ 10 4}

and one more 17 { with {x 5} {+ x { with {x 3} 10}}} -- > {+ 5 { with {x 3} 10}} -- > {+ 5 10}

However, if we try this: 18 { with {x 5} {+ x { with {x 3} x }}} we get: 19 -- > {+ 5 { with {x 3} 5}} -- > {+ 5 5} -- > 10 = wrong

In the above example, we want the inner with to shadow the outer with s binding for x. [substitution, take 3] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all instances of i that are not themselves binding instances, and that are not in any nested scope, with the expression v.

This new rule avoids bad substitution above, but it is now doing things too carefully: 20 { with {x 5} {+ x { with {y 3} x }}} becomes -- > {+ 5 { with {y 3} x}} -- > {+ 5 x} which is an error because x is unbound (and there is no reasonable rule that we can specify to evaluate it).

This new rule avoids bad substitution above, but it is now 2017-03-02 doing things too carefully: 20 { with {x 5} {+ x { with {y 3} x }}} becomes -- > {+ 5 { with {y 3} x}} -- > {+ 5 x} which is an error because x is unbound (and there is no reasonable rule that we can specify to evaluate it). 1. The problem is that our substitution halts at every new scope, in this case, it stopped at the new y scope, but it shouldn t have because it uses a different name. In fact, that last definition of substitution cannot handle any nested scope.

Finally, maybe correct: [substitution, take 4] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all instances of i that are not themselves binding instances, and that are not in any nested scope of i, with the expression v. We can shorten this using the definition of free [substitution, take 4b] e[v/i] To substitute an identifier i in an expression e with an expression v, replace all instances of i that are free in e with the expression v.

21 ; returns expr [ to/ from ]. ; leaves no free occurences of `to ' ( define ( subst expr from to) ( type- case WAE expr [ Add (l r) ( Add ( subst l from to) ( subst r from to))] [Id ( name ) (if (eq? name from ) to expr )] [ With ( bound- id named- expr bound- body ) (if (eq? bound-id from ) expr ; <-- don ' t go in! ( With bound- id named- expr ( subst bound-body from to)))]))

this is just as good as writing a formal paper version of the substitution rule. Or maybe better but we still have bugs! Before we find the bugs, we need to see when and how substitution is used in the evaluation process. To modify our evaluator, we will need rules to deal with the new syntax pieces with expressions and identifiers.

evaluating with When we see an expression that looks like: 22 { with {x E1} E2} we continue by evaluating E1 to get a value V1, we then substitute the identifier x with the expression V1 in E2, and continue by evaluating this new expression. In other words, we have the following evaluation rule: 23 eval ( { with {x E1} E2} ) = eval ( E2[ eval (E1)/x] )

What about identifiers? The main feature of subst, as said in the purpose statement, is that it leaves no free instances of the substituted variable around. This means that if the initial expression is valid (did not contain any free variables), then substition removes all identifiers. 24 { with {x E1} E2} to 25 E2[ E1/x ]

We can now extend the eval rule of AE to WAE: 26 eval (...) =... same as the AE rules... eval ({ with {x E1} E2 }) = eval (E2[ eval (E1)/x]) eval (id) = error! The above rules are easily coded as follows: 27 ( define ( eval expr ) ( type- case WAE expr. [ With ( bound- id named- expr bound- body ) ( eval ( subst bound- body bound- id ( Num ( eval named-expr ))))]. [Id ( name ) ( error 'eval " free identifier ")]))

We can now extend the eval rule of AE to WAE: 2017-03-02 26 eval (...) =... same as the AE rules... eval ({ with {x E1} E2 }) = eval (E2[ eval (E1)/x]) eval (id) = error! The above rules are easily coded as follows: 27 ( define ( eval expr ) ( type-case WAE expr. [ With ( bound-id named-expr bound-body ) ( eval ( subst bound-body bound-id ( Num ( eval named-expr ))))]. [Id ( name ) ( error 'eval " free identifier ")])) 1. If you re paying close attention, you might catch a potential problem in this definition: we re substituting eval(e1) for x in E2 an operation that requires a WAE expression, but eval(e1) is a number. (Look at the type of the eval definition we had for AE, then look at the above definition of subst.) This seems like being overly pedantic, but we it will require some resolution when we get to the code.

Here are a few test cases. 28 '5 '{+ 5 5} '{ with {x {+ 5 5}} {+ x x}} '{ with {x 5} {+ x x}} '{ with {x 5} {+ x { with {x 3} 10}}} '{ with {x 5} {+ x { with {x 3} x }}} '{ with {x 5} {+ x { with {y 3} x }}} '{ with {x 1} y}

Here are a few test cases. 2017-03-02 28 '5 '{+ 5 5} '{ with {x {+ 5 5}} {+ x x}} '{ with {x 5} {+ x x}} '{ with {x 5} {+ x { with {x 3} 10}}} '{ with {x 5} {+ x { with {x 3} x }}} '{ with {x 5} {+ x { with {y 3} x }}} '{ with {x 1} y} 1. Note the Num expression in the marked line: evaluating the named expression gives us back a number we need to convert this number into a syntax to be able to use it with subst. The solution is to use Num to convert the resulting number into a numeral (the syntax of a number). It s not an elegant solution, but it will do for now.

What about these cases? 29 ( test ( run '{ with {x {+ 5 5}} { with {y {- x 3}} {+ y y }}}) 14) ( test ( run '{ with {x 5} { with {y {- x 3}} {+ y y }}}) 4) ( test ( run '{ with {x 5} { with {y x} y }}) 5) ( test ( run '{ with {x 5} { with {x x} x }}) 5)

In expressions like: 30 { with {x 5} { with {y x} y}} we forgot to substitute x in {with {y x}...} We need to the recursive substitute in both the with s named-expression as well as its body.

31 ;; expr [ to/from ] ( define ( subst expr from to) ( type- case WAE expr. [ With ( bound- id named- expr bound- body ) (if (eq? bound-id from ) expr ( With bound- id ( subst named- expr from to) ; new ( subst bound- body from to)))]))

And still we have a problem... Now it s 32 { with {x 5} { with {x x} x}} that halts with an error, but we want it to evaluate to 5 When we substitute 5 for the outer x, we don t go inside the inner with because it has the same name but we do need to go into its named expression. We need to substitute in the named expression even if the identifier is the same one we substituting.

33 ;; expr [ to/from ] ( define ( subst expr from to) ( type- case WAE expr. [( Id name ) (if (eq? name from ) to expr )] [( With bound- id named- expr bound- body ) ( With bound- id ( subst named- expr from to) (if (eq? bound-id from ) bound- body ( subst bound-body from to)))]))

34 ( test ( run '5) 5) ( test ( run '{+ 5 5}) 10) ( test ( run '{ with {x {+ 5 5}} {+ x x }}) 20) ( test ( run '{ with {x 5} {+ x x }}) 10) ( test ( run '{ with {x {+ 5 5}} { with {y {- x 3}} {+ y y }}}) 14) ( test ( run '{ with {x 5} { with {y {- x 3}} {+ y y }}}) 4) ( test ( run '{ with {x 5} {+ x { with {x 3} 10}}}) 15) ( test ( run '{ with {x 5} {+ x { with {x 3} x }}}) 8) ( test ( run '{ with {x 5} {+ x { with {y 3} x }}}) 10) ( test ( run '{ with {x 5} { with {y x} y }}) 5) ( test ( run '{ with {x 5} { with {x x} x }}) 5) ( test/exn ( run '{ with {x 1} y}) " free identifier ")