Comp 411 Principles of Programming Languages Lecture 7 Meta-interpreters. Corky Cartwright January 26, 2018

Similar documents
Data Abstraction. An Abstraction for Inductive Data Types. Philip W. L. Fong.

Comp 311: Sample Midterm Examination

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

Functional Programming. Pure Functional Programming

CS 314 Principles of Programming Languages. Lecture 21

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

CMSC 330: Organization of Programming Languages. Operational Semantics

Functional Programming. Pure Functional Languages

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

Functional Programming. Pure Functional Languages

Principles of Programming Languages

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

C311 Lab #3 Representation Independence: Representation Independent Interpreters

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

Formal Systems and their Applications

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

CS 242. Fundamentals. Reading: See last slide

The Eval/Apply Cycle Eval. Evaluation and universal machines. Examining the role of Eval. Eval from perspective of language designer

Deferred operations. Continuations Structure and Interpretation of Computer Programs. Tail recursion in action.

Lambda the Ultimate. Corky Cartwright Vivek Sarkar Department of Computer Science Rice University

Principles of Programming Languages

Note that in this definition, n + m denotes the syntactic expression with three symbols n, +, and m, not to the number that is the sum of n and m.

Procedures. EOPL3: Section 3.3 PROC and App B: SLLGEN

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

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

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

Turtles All The Way Down

Scheme in Scheme: The Metacircular Evaluator Eval and Apply

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

CS 314 Principles of Programming Languages

Lecture 12: Conditional Expressions and Local Binding

A Brief Introduction to Scheme (II)

CS 342 Lecture 8 Data Abstraction By: Hridesh Rajan

Lambda Calculus. Gunnar Gotshalks LC-1

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

axiomatic semantics involving logical rules for deriving relations between preconditions and postconditions.

Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

CVO103: Programming Languages. Lecture 5 Design and Implementation of PLs (1) Expressions

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

CMSC 330: Organization of Programming Languages

CSE341, Spring 2013, Final Examination June 13, 2013

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

User-defined Functions. Conditional Expressions in Scheme

Introduction to lambda calculus Part 3

Project 2: Scheme Interpreter

TAIL RECURSION, SCOPE, AND PROJECT 4 11

Streams, Delayed Evaluation and a Normal Order Interpreter. CS 550 Programming Languages Jeremy Johnson

Announcements. The current topic: Scheme. Review: BST functions. Review: Representing trees in Scheme. Reminder: Lab 2 is due on Monday at 10:30 am.

Lexical vs. Dynamic Scope

A Brief Introduction to Scheme (I)

Lecture 15 CIS 341: COMPILERS

Lambda Calculus and Lambda notation in Lisp II. Based on Prof. Gotshalks notes on Lambda Calculus and Chapter 9 in Wilensky.

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

Functional abstraction

CSE341 Spring 2017, Final Examination June 8, 2017

Implementing Continuations

An Explicit Continuation Evaluator for Scheme

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

CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan

Denotational Semantics. Domain Theory

Lambda Calculus see notes on Lambda Calculus

Basic Scheme February 8, Compound expressions Rules of evaluation Creating procedures by capturing common patterns

Organization of Programming Languages CS3200/5200N. Lecture 11

The Substitution Model

Lambda Calculus LC-1

CSE341 Autumn 2017, Final Examination December 12, 2017

Scope and Introduction to Functional Languages. Review and Finish Scoping. Announcements. Assignment 3 due Thu at 11:55pm. Website has SML resources

Programming Languages Third Edition

Lecture 3: Expressions

CS 6110 S11 Lecture 12 Naming and Scope 21 February 2011

Comp 411 Principles of Programming Languages Lecture 3 Parsing. Corky Cartwright January 11, 2019

Functional Programming. Big Picture. Design of Programming Languages

Last class. CS Principles of Programming Languages. Introduction. Outline

11/6/17. Functional programming. FP Foundations, Scheme (2) LISP Data Types. LISP Data Types. LISP Data Types. Scheme. LISP: John McCarthy 1958 MIT

3.7 Denotational Semantics

Konzepte von Programmiersprachen

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

Foundations. Yu Zhang. Acknowledgement: modified from Stanford CS242

Documentation for LISP in BASIC

1.3. Conditional expressions To express case distinctions like

From Syntactic Sugar to the Syntactic Meth Lab:

CSCC24 Functional Programming Scheme Part 2

Programming Languages

RACKET BASICS, ORDER OF EVALUATION, RECURSION 1

Below are example solutions for each of the questions. These are not the only possible answers, but they are the most common ones.

Concepts of programming languages

CSE341 Spring 2017, Final Examination June 8, 2017

An Explicit-Continuation Metacircular Evaluator

Write a procedure powerset which takes as its only argument a set S and returns the powerset of S.

Handout 10: Imperative programs and the Lambda Calculus

Scheme. Functional Programming. Lambda Calculus. CSC 4101: Programming Languages 1. Textbook, Sections , 13.7

LECTURE 16. Functional Programming

Introduction to Scheme

Parsing Scheme (+ (* 2 3) 1) * 1

Lecture 5 - Axiomatic semantics

Coq projects for type theory 2018

(Refer Slide Time: 4:00)

cs173: Programming Languages Midterm Exam

Recursive Functions of Symbolic Expressions and Their Computation by Machine Part I

Contents. Chapter 1 SPECIFYING SYNTAX 1

Transcription:

Comp 411 Principles of Programming Languages Lecture 7 Meta-interpreters Corky Cartwright January 26, 2018

Denotational Semantics The primary alternative to syntactic semantics is denotational semantics. A denotational semantics maps abstract syntax trees to a set of denotations (mathematical values like numbers, lists, and functions). Simple denotations like numbers and lists are essentially the same mathematical objects as syntactic values: they have simple inductive definitions with exactly the same structure as the corresponding abstract syntax trees. But denotations can also be complex mathematical objects like functions or sets. For example, the denotation for a lambda-expression in pure (functional) Scheme is a function mapping denotations to denotations--not some syntax tree as in a syntactic semantics.

Meta-interpreters Denotational semantics is rooted in mathematical logic: the semantics of terms (expressions) in the predicate calculus is defined denotationally by recursion on the syntactic structure of terms. The meaning of each term is a value in an mathematical structure (as used in first-order logic). In the realm of programming languages, purely functional interpreters (defined by pure recursion on the structure of ASTs) constitute a restricted form of denotational definition. The defect is that the output of an actual interpreter is restricted to values that can be characterized syntactically. (How do you output a function?) On the other hand, interpreters naturally introduce a simple form of functional abstraction. A recursive interpreter accepts an extra input, an environment mapping free variables to values, thus defining the meaning of a program expression as a function from environments to values. Syntactic interpreters are not denotational because they transform ASTs. A denotational interpreter uses pure structural recursion. To handle the bindings to variables, it cannot perform substitutions; it must maintain an environment of bindings instead.

Meta-interpreters cont. Interpreters written in a denotational style are often called meta-interpreters because they are defined in a meta-mathematical framework where programming language expressions and denotations are objects in the framework. The definition of the interpreter is a level above definitions of functions in the language being defined. In mathematical logic, meta-level definitions are expressed informally as definitions of mathematical functions. In program semantics, meta-level definitions are expressed in a convenient functional framework with a semantics that is easily defined and understood using informal mathematics. Formal denotational definitions are written in a mathematical meta-language corresponding to some formulation of a Universal Domain (a mathematical domain in which all relevant programming language domains can be simply embedded, usually as projections). This material is subject of a graduate level course on domain theory. A functional interpreter for language L written in a functional subset of L is called a meta-circular interpreter. It really isn't circular because it reduces the meaning of all programs to a single purely functional program which can be understood independently using simple mathematical machinery (inductive definitions over familiar mathematical domains).

Denotational Building Blocks Inductively defined ASTs for program syntax. We have thoroughly discussed this topic. What about denotations? For now, we will only use simple inductively defined values (without functional abstraction) like numbers, lists, tuples, etc. What about environments? Mathematicians like to use functions. An environment is a function from variables to denotations. But environment functions are special because they are finite. Software engineers prefer to represent them as lists of pairs binding variables to denotations. In higher-order languages, functions are data objects. How do we represent them? For now we will use ASTs possibly supplemented by simple denotations (as described above).

Critique of Deferred Substitution Interpreter from Lecture 6 How did we represent the denotations of lambdaexpressions (functions) in environments? By their ASTs. Is this implementation correct? No! Counterexample: (let ([twice (lambda (f) (lambda (x) (f (f x))))]) (let ([x 5]) ((twice (lambda (y) (+ x y))) 0)))

Evaluate (syntactically) (let [(twice (lambda (f) (lambda (x) (f (f x)))))] (let [(x 5)] (twice (lambda (y) (+ x y))) 0)) (let [(x 5)] ((lambda (f) (lambda (x) (f (f x)))) (lambda (y) (+ x y))) 0)) ((lambda (f) (lambda (x) (f (f x)))) (lambda (y) (+ 5 y))) 0) ((lambda (x) ((lambda (y) (+ 5 y)) ((lambda (y) (+ 5 y)) x)))) 0) ((lambda (y) (+ 5 y)) ((lambda (y) (+ 5 y)) 0)) ((lambda (y) (+ 5 y)) (+ 5 0)) ((lambda (y) (+ 5 y)) 5) (+ 5 5) 10

Evaluate (using our interpreter) (let [(twice (lambda (f) (lambda (x) (f (f x)))))] (let (x 5)] (twice (lambda (y) (+ x y))) 0)) { twice = (lambda (f) (lambda (x) (f (f x)))) } (let [(x 5)] ((twice (lambda (y) (+ x y))) 0)) { x = 5, twice = (lambda (f) (lambda (x) (f (f x)))) } ((twice (lambda (y) (+ x y))) 0) { x = 5,... } (((lambda (f) (lambda (x) (f (f x)))) (lambda (y) (+ x y))) 0) { f = (lambda (y) (+ x y)), x = 5,... } ((lambda (x) (f (f x))) 0) { x = 0, f = (lambda (y) (+ x y)),... } (f (f x)) { x = 0, f = (lambda (y) (+ x y)),... } ((lambda (y) (+ x y)) (f x)) { x = 0,... } ((lambda (y) (+ x y)) ((lambda (y) (+ x y)) x)) { x = 0,... } ((lambda (y) (+ x y)) ((lambda (y) (+ x y)) 0)) { y = 0, x = 0,... } ((lambda (y) (+ x y)) (+ x y)) { y = 0, x = 0,... } ((lambda (y) (+ x y)) (+ 0 y)) { y = 0, x = 0,... } ((lambda (y) (+ x y)) (+ 0 0)) { y = 0, x = 0,... } ((lambda (y) (+ x y)) 0) { y = 0, y = 0, x = 0,... } (+ x y) { y = 0,... } (+ 0 y) {... } (+ 0 0) 0

Closures Are Essential Exercise: evaluate the same expression using our broken interpreter. The computed answer is 0! The interpreter uses the wrong binding for the free variable x in (lambda (y) (+ x y)). The environment records deferred substitutions. When we pass a function as an argument, we need to pass a package including the deferred substitutions. Why? The function will be applied in a different environment which may associate the wrong bindings it free variables. In the PL (programming languages) literature, these packages (code representation, environment) are called closures. Note the similarity between this mistake and the capture of bound variables. Unfortunately, this mistake has been labeled as a feature rather than a bug in much of the PL literature. It is called dynamic scoping rather than a horrendous mistake. Watch out whenever you must program in a language with dynamic scoping.

Correct Semantic Interpretation (define-struct (closure proc env)) ;; V = Const Closure ; revises our former definition of V ;; Binding = (make-binding Sym V) ; Note: Sym not Var ;; Env = (listof Binding) ;; Closure = (make-closure Proc Env) ;; R Env V (define eval (lambda (M env) (cond ((var? M) (lookup (var-name M) env)) ((const? M) M) ((proc? M)) (make-closure M env)) ((add? M) ; M has form (+ l r) (add (eval (add-left M) env) (eval (add-right M) env))) (else ; M has form (N1 N2) (apply (eval (app-rator M) env) (eval (app-rand M) env)))))) ;; Closure V V (define apply (lambda (cl v) ; assume cl is a closure (eval (proc-body (closure-proc cl)) (cons (make-binding (proc-param (closure-proc cl)) v) (closure-env cl)))