CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan

Similar documents
CS 342 Lecture 8 Data Abstraction By: Hridesh Rajan

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

User-defined Functions. Conditional Expressions in Scheme

CS 342 Lecture 6 Scheme Procedures and Closures By: Hridesh Rajan

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

CS61A Midterm 2 Review (v1.1)

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

Introduction to Scheme

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

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

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

Functional abstraction

Functional Programming. Pure Functional Languages

Racket. CSE341: Programming Languages Lecture 14 Introduction to Racket. Getting started. Racket vs. Scheme. Example.

Comp 311: Sample Midterm Examination

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


Fall 2018 Discussion 8: October 24, 2018 Solutions. 1 Introduction. 2 Primitives

Introduction to Functional Programming in Racket. CS 550 Programming Languages Jeremy Johnson

Functional Programming. Pure Functional Programming

An introduction to Scheme

A Brief Introduction to Scheme (II)

Organization of Programming Languages CS3200/5200N. Lecture 11

An Introduction to Scheme

;;; Determines if e is a primitive by looking it up in the primitive environment. ;;; Define indentation and output routines for the output for

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

Intro. Scheme Basics. scm> 5 5. scm>

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Documentation for LISP in BASIC

CSCC24 Functional Programming Scheme Part 2

Notes on Higher Order Programming in Scheme. by Alexander Stepanov

Control Structures. Outline. In Text: Chapter 8. Control structures Selection. Iteration. Gotos Guarded statements. One-way Two-way Multi-way

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

CS 314 Principles of Programming Languages

CS 314 Principles of Programming Languages

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

More Scheme CS 331. Quiz. 4. What is the length of the list (()()()())? Which element does (car (cdr (x y z))) extract from the list?

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

SCHEME AND CALCULATOR 5b

Functional Programming - 2. Higher Order Functions

Discussion 4. Data Abstraction and Sequences

C311 Lab #3 Representation Independence: Representation Independent Interpreters

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

CS61A Notes 02b Fake Plastic Trees. 2. (cons ((1 a) (2 o)) (3 g)) 3. (list ((1 a) (2 o)) (3 g)) 4. (append ((1 a) (2 o)) (3 g))

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Haskell Functions

Principles of Programming Languages Topic: Scope and Memory Professor Louis Steinberg Fall 2004

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

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.

University of Massachusetts Lowell

Special Directions for this Test

Structure of Programming Languages Lecture 5

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

CONCEPTS OF PROGRAMMING LANGUAGES Solutions for Mid-Term Examination

Scheme Quick Reference

FOFL and FOBS: First-Order Functions

Scheme Quick Reference

CS 314 Principles of Programming Languages

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

CSSE 304 Assignment #13 (interpreter milestone #1) Updated for Fall, 2018

Principles of Programming Languages Topic: Functional Programming Professor L. Thorne McCarty Spring 2003

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

Streams and Evalutation Strategies

CSE 341: Programming Languages

LECTURE 16. Functional Programming

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

CS1102: Macros and Recursion

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science

G Programming Languages - Fall 2012

CS 415 Midterm Exam Spring SOLUTION

Programming Languages

From Syntactic Sugar to the Syntactic Meth Lab:

CSCI-GA Scripting Languages

CSE341: Programming Languages Lecture 15 Macros. Zach Tatlock Winter 2018

Lecture 3: Expressions

What is a macro. CSE341: Programming Languages Lecture 15 Macros. Example uses. Using Racket Macros. Zach Tatlock Winter 2018

MORE SCHEME. 1 What Would Scheme Print? COMPUTER SCIENCE MENTORS 61A. October 30 to November 3, Solution: Solutions begin on the following page.

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

Typed Racket: Racket with Static Types

Scheme as implemented by Racket

Chapter 1. Fundamentals of Higher Order Programming

Scheme Tutorial. Introduction. The Structure of Scheme Programs. Syntax

Lecture08: Scope and Lexical Address

An Explicit Continuation Evaluator for Scheme

Scheme Basics > (butfirst '(help!)) ()

COP4020 Programming Assignment 1 - Spring 2011

Project 5 - The Meta-Circular Evaluator

G Programming Languages - Fall 2012

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

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Principle of Complier Design Prof. Y. N. Srikant Department of Computer Science and Automation Indian Institute of Science, Bangalore

Recursion & Iteration

Racket: Modules, Contracts, Languages

PROBLEM SOLVING 11. July 24, 2012

Informal Semantics of Data. semantic specification names (identifiers) attributes binding declarations scope rules visibility

Func+on applica+ons (calls, invoca+ons)

Repetition Through Recursion

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d)

Transcription:

CS 342 Lecture 7 Syntax Abstraction By: Hridesh Rajan 1 Reading SICP, page 11-19, Section 1.1.6 Little Schemer, Chapter 2 2 The Idea of Syntax Abstraction Problem. Often programming tasks are repetitive, in that users (programmers) are doing same thing over and over again. Following are some examples of this repetitive tasks. Making local bindings, deeply nesting if expressions, iterating over a collection, accessing collection as an array, etc. However, repetitive tasks are often tedious and error-prone. For example, local bindings can result in problems with inadvertant usage of unintended variables, deeply nesting if expressions can lead to logical errors, iterating over a collection often causes problems such as incorrect encoding of iterating over elements in the collection, explicitly programming access to a collection as an array often leaves inadvertant errors that result in array out of bound exceptions that only show up in corner cases, etc. Solution. The common solution is to introduce new syntax that captures the pattern. The semantics for this new syntax can be given by translation to the original syntax. Examples. Some examples of syntax abstraction in Scheme are let, letrec, and, or, cond, case. Some examples of syntax abstraction in C++/Java are for loops, foreach loops. Some examples of syntax abstraction in C# are delegate, indexers, property, etc. Terms. There are four essential and related terms with respect to this idea: syntactic sugar, syntax abstraction, macro, and desugaring. Be sure to understand what each of these mean. Exercise 2.1 How is this like procedural abstraction? A syntactic abstraction also encapsulates a pattern of computation, but doesn t need special syntax as a procedure works. Exercise 2.2 Which is the sugar, the shorter, less tedious version, or the translation? For the purposes of studying programming languages, it is important that we can boil away the sugars and be left with the essentials of a langauge, which is what we ll focus on studying in this course. 1

3 Logical Connectives Problem. The main issue with writing the code for emulate logical connectives is that it verbose and repetitive. For example, consider a function that evaluates to true when an element is a member of a list and false otherwise. (define member? (lambda (e ls) (if (not (null? ls)) (if (equal? e (car ls)) #t (member? e (cdr ls))) #f))) With logical connectives as syntactic abstraction in the language this could be written much more succintly. (define member? (lambda (e ls) (and (not (null? ls)) (or (equal? e (car ls)) (member? e (cdr ls)))))) There are two terms with respect to logical connectives that deserve mention namely: short-circuit evaluation and conditional evaluation. Exercise 3.3 Can or evaluate all its arguments? What would happen? No, it would loop. This shows the importance of short-circuit evaluation 3.1 Syntax and Semantics of Logical Connectives The following grammar defines the syntax of logical connectives. <expression> ::= (or {<expression>}*) (and {<expression>}*)... As mentioned before, semantics can be defined by translation to the original language (desugaring). Following listing shows an example. Note the use of variable arity procedures from the previous lectures. (or ) = #f (or e1 e2...) = (if e1 #t (or e2...)) (and ) = #t (and e1 e2...) = (if e1 (and e2...) #f) Exercise 3.4 What s this like in other languages? in C? in Pascal? and && in C, not and &. Pascal has no equivalent. cor, cand in Algol 68? and_then, or_else in Ada. Exercise 3.5 Rewrite the following using "and" and "or" 2

(define foo? (lambda (x) (if (pair? x) (if (number? (car x)) #t (number? (cdr x))) #f))) Exercise 3.6 Define the predicate two-elem-list? to test whether a datum is a list of length two. 4 Cond Expression Problem. Usually nested ifs are hard to read. They also do not match the structure of input very well. For example, consider the following listing that count the number of variable references in an expression. (define count-varrefs (lambda (exp) (if (varref? exp) 1 (if (lambda? exp) (count-varrefs (lambda->body exp)) (+ (count-varrefs (app->rator exp)) (count-varrefs (app->rand exp))))))) (define count-varrefs (lambda (exp) An alternative implementation using the cond expression is shown in the following listing. Note that this matches the structure of the input (exp) well. As an exercise, independently produce the outline of this procedure by following the strategy defined in the lecture on recursion over flat lists. (define count-varrefs (lambda (exp) (cond ((varref? exp) 1) ((lambda? exp) (count-varrefs (lambda->body exp))) (else (+ (count-varrefs (app->rator exp)) (count-varrefs (app->rand exp))))))) Exercise 4.7 How would you write this in C or C++? C++ doesn t really provide something like cond, switch is not really the same, although similar but people make do with formatting conventions. Some languages, like Algol 68 and CLU, provide elseif as a keyword. An example of the usage of the elseif construct is provided in the following listing. if b then s1() elseif b2 then s2() else s3() end 3

4.1 Syntax of Cond Expression The following grammar shows the syntax of cond expression. Note the use of Kleene plus ( + / one or more) instead of Kleene star (* / zero or more) in the production for expressions. <expression> ::= ( cond {<cond-clause>}+ <else-clause> ) <cond-clause> ::= ( <test> <body> ) <else-clause> ::= ( else <body> ) <test> ::= <expression> <body> ::= <expression> 4.2 Translation-based Semantics of Cond Expression (cond (test1 body1) (test2 body2)... (testn bodyn) (else bodye)) Actually, a body can have any number of expressions, there s an implicit begin there. 5 Case Expression Problem. comparing the value of expression to many numbers, chars, symbols can be tedious. For example, consider the following listing that uses the cond expression. (let ((pos (list-index x ls))) (cond ((= pos 0) first) ((= pos 1) second) ((= pos 2) third) ((or (= pos 3) (= pos 4)) higher) (else too-high))) An alternative implementation that uses case expression could be the following. (case (list-index x ls) ((0) first) ((1) second) ((2) third) ((3 4) higher) (else too-high)) Exercise 5.8 What s this like in C/C++? In Pascal? This is like switch, Pascal s case Exercise 5.9 What s different about this from the C/C++ statement? 4

It is an expression, therefore no break is needed. This helps avoid a common error in the use of switch statements. Historic Perspective. The case expression was invented by Tony Hoare. Pascal was the first language to have this feature. Usually this has very fast implementation, typically uses indirect branch through jump table based on value. 5.1 Syntax of Case Expression The following shows the grammar for case expressions. <expression> ::= ( case <expression> {<case>}+ <else-clause> ) <case> ::= ( <key-list> <body> ) <key-list> ::= ( {<key>}+ ) <key> ::= <symbol> <number> <char> <else-clause> ::= ( else <body> ) <body> ::= <expression> 5.2 Translation-based Semantics of Case Expressions (let ((*key* e)) (case e (cond (kl1 b1) ((memv *key* kl1) b1) (kl2 b2) ((memv *key* kl2) b2)...... (kln bn) ((memv *key* kl3) b3) (else be)) (else be))) 6 Local Bindings For this section, you are encouraged to see R5RS for details. The required reading for this section is SICP pp. 63 64. 6.1 The Problems Solved by Let (double-reverse-sublist ((a b) (c d))) ==> ((b a) (b a) (d c) (d c)) (define double-reverse-sublist (lambda (lst) (if (null? lst) () (cons (reverse (car lst)) (cons (reverse (car lst)) (double-reverse-sublist (cdr lst))))))) The main problems with this code are the following. loss of time by repeating computations less clear, tedious to repeat code with side effects, need to save value 5

Exercise 6.10 What s the time complexity of reverse? Exercise 6.11 How much slower is the code than it would be if didn t have to repeat that computation? Exercise 6.12 What mechanism do we have, so far, that binds names to values? 6.2 Rewrites to Avoid Repeated Computation (define double-reverse-sublist (lambda (lst) (if (null? lst) () (cons-twice (reverse (car lst)) (double-reverse-sublist (cdr lst)))))) (define cons-twice (lambda (elem ls) (cons elem (cons elem ls)))) But it s annoying to have to use a helping procedure. We could eliminate it, since it s only used once, by substituting its definition. 6.3 Substituting the Definition of cons-twice (define double-reverse-sublist (lambda (lst) (if (null? lst) () ((lambda (elem ls) (cons elem (cons elem ls))) (reverse (car lst)) (double-reverse-sublist (cdr lst)))))) But this is hard to read. Why? Exercise 6.13 What do other languages do to avoid recomputation? Variables, assignment, constant declarations. Primary reason is that it is better for humans to have the names, like elem and ls, bound to values first, close to each other. 6.4 Using Let In Scheme similar effect can be achieved by using the let expression. An example rewrite of the double reverse sublist function using let follows. 6

(define double-reverse-sublist (lambda (lst) (if (null? lst) () (let ( (elem (reverse (car lst))) (ls (double-reverse-sublist(cdr lst))) ) (cons elem (cons elem ls)) ) ) ) ) An alternative implementation is as follows. (define double-reverse-sublist (lambda (lst) (if (null? lst) () (let ((elem (reverse (car lst)))) (cons elem (cons elem (double-reverse-sublist (cdr lst)))) )))) So this is the idea, we define the meaning of let by translating it back to that hard-to-read lambda application, humans get nice syntax, language gets simple semantics Exercise 6.14 What is this like in other langauges? Declaration forms (in C, Pascal), so this is a kind of local declaration construct. 6.5 Syntax of Let <Scheme-exp> ::= (let ({<binding>}+) <body>) <binding> ::= (<var> <Scheme-exp>) <body> ::= <Scheme-exp> The production for <body> is a bit of a lie, it s actually a list of <Scheme-exp>, but we won t see why until we discuss side effects. 6.6 Semantics of Let (let ((var1 exp1)... (varn expn)) body) = ((lambda (var1... varn) body) exp1... expn) This is where the two parentheses come from. To illustrate the process, let us look at an example desugar of the following function using let expression. (define addem (lambda (x y z) (let ((x (+ y z)) (y (+ x 0))) (+ x y)))) 7

which desugars into the following function without let expression. (define addem2 (lambda (x y z) ((lambda (x y) (+ x y)) (+ y z) (+ x 0)))) Exercise 6.15 What is this equal to? Exercise 6.16 [For You To Do (IN PAIRS)] What is the difference between the following? (define x 9) (let ((x 3)) and (let ((y (+ x 4))) (* x y))) (define x 9) (let ((x 3) (y (+ x 4))) (* x y)) Hints: It may help to convert each of these into their desugared form? It also helps to draw arrows from variable references to the variable declarations. Answer: In the first one, the x s refer to 3, while in the second let, the x refers to 9 so the first is 21, the second is 33. Exercise 6.17 How would you define the region of a var decl in a let? the scope? 7 The LETREC Expression The reading for this section is SICP p. 391 and EOPL p. 23. 7.1 What does let helps us with? It helps us avoid namespace clutter. Names are defined closer to where they are used and their scope is limited. (define circle-area (let ((pi 3.14159)) (lambda (radius) (* pi (* radius radius))))) So you might like to do that for helping procedures. Why? It would certainly help with managing procedure names in the program, if we don t need the helping procedures else where in the program. 8

7.2 Does Let Work for Procedures? (define list-product (let ((prod-iter (lambda (lon acc) (if (null? lon) acc (if (zero? (car lon)) 0 (prod-iter (cdr lon) (* acc (car lon))))))))) (lambda (lon) (prod-iter lon 1)))) Exercise 7.18 What does the recursive call of prod-iter refer to? Nothing! Exercise 7.19 Why doesn t that work? The scope of prod-iter doesn t include it s body! 7.3 We Need to Introduce a New Expression (LETREC) (define list-product (letrec ((prod-iter (lambda (lon acc) (if (null? lon) acc (if (zero? (car lon)) 0 (prod-iter (cdr lon) (* acc (car lon))))))))) (lambda (lon) (prod-iter lon 1)))) 7.4 Syntax <Scheme-exp> ::= (letrec ({<pbinding>}+) <body>) <pbinding> ::= (<var> <lambda-exp>) <lambda-exp> ::= (lambda <formals> <body>) <formals> ::= ({<var>}*) <var> <body> ::= <Scheme-exp> Exercise 7.20 How are local declarations of recursive procedures handled in other langauges? For example, Pascal, ML. Exercise 7.21 Is there any difference from Scheme? They aren t allowed within expressions in other languages. 9

Exercise 7.22 [FOR YOU TO DO (IN PAIRS)] Using letrec define a combination of mutually recursive functions even? and odd? with the expected semantics. (letrec ((odd? (lambda (n) (and (not (zero? n)) (even? (- n 1))))) (even? (lambda (n) (or (zero? n) (odd? (- n 1)))))) (list (odd? 227) (even? 342))) ==> (#t #t) % 7.5 Advantages and Disadvantages of Using LETREC The main disadvantage of using letrec expression is that it may be harder to debug the internal procedures. The advantages are as follows. namespace control avoid passing often optimal (as it can avoid passing arguments) Exercise 7.23 [DO-IN-PAIRS] Using a letrec, define the function expt, which takes two numbers x and y and computes x y. (expt 4 0) = 1 (expt 3 1) = 3 (expt 3 2) = 9 (expt 3 3) = 27 Exercise 7.24 What is the region of a variable definition in a let compared to a letrec? In a let the region of the variable declarations v1 = E1, v2 = E2,..., vn = En includes only the body expression. Whereas, in a letrec the region of the variable declarations v1, v2,..., vn includes the expressions E1, E2,..., En, and the body expression. Exercise 7.25 What are the advantages of using letrec? 10

There are several advantages. First, it is sometimes faster (as it can avoid passing arguments repeatedly). Second, it allows control of global namespace, and third, it allows access control (no one outside can use local procedure) Exercise 7.26 Any disadvantages? It is a bit difficult to test the local helping procedures! 11