Optional 1: let is sugar

In Scheme, source code is data. Every non-atomic expression is written as a Scheme list, so we can write procedures that manipulate other programs just as we write procedures that manipulate lists.

Rewriting programs can be useful: we can write an interpreter that only handles a small core of the language, and then write a procedure that converts other special forms into the core language before a program is passed to the interpreter.

For example, the let special form is equivalent to a call expression that begins with a lambda expression. Both create a new frame extending the current environment and evaluate a body within that new environment.

(let ((a 1) (b 2)) (+ a b)) ;; Is equivalent to: ((lambda (a b) (+ a b)) 1 2)

These expressions can be represented by the following diagrams:

Let Lambda
let lambda

Use this rule to implement a procedure called let-to-lambda that rewrites all let special forms into lambda expressions. If we quote a let expression and pass it into this procedure, an equivalent lambda expression should be returned:

scm> (let-to-lambda '(let ((a 1) (b 2)) (+ a b))) ((lambda (a b) (+ a b)) 1 2) scm> (let-to-lambda '(let ((a 1)) (let ((b a)) b))) ((lambda (a) ((lambda (b) b) a)) 1) scm> (let-to-lambda 1) 1 scm> (let-to-lambda 'a) a

In order to handle all programs, let-to-lambda must be aware of Scheme syntax. Since Scheme expressions are recursively nested, let-to-lambda must also be recursive. In fact, the structure of let-to-lambda is somewhat similar to that of scheme_eval--but in Scheme! As a reminder, atoms include numbers, booleans, nil, and symbols. You do not need to consider code that contains quasiquotation for this problem.

(define (let-to-lambda expr) (cond ((atom? expr) <rewrite atoms>) ((quoted? expr) <rewrite quoted expressions>) ((lambda? expr) <rewrite lambda expressions>) ((define? expr) <rewrite define expressions>) ((let? expr) <rewrite let expressions>) (else <rewrite other expressions>)))

Hint: Consider how you can use map to convert let forms in every element of a list to the equivalent lambda form.

scm> (zip '((1 2) (3 4) (5 6))) ((1 3 5) (2 4 6)) scm> (zip '((1 2))) ((1) (2)) scm> (zip '()) (() ())

Hint 2: In this problem, it may be helpful to build a scheme list that evaluates to a special form (for instance, a lambda expression). As a related example, the following code builds a scheme list that evaluates to the expression (define (f x) (+ x 1)):

Test your implementation by running

Use Ok to test your code:

python ok -q optional_1

We used let while defining let-to-lambda. What if we want to run let-to-lambda on an interpreter that does not recognize let? We can pass let-to-lambdato itself to rewrite itself into an equivalent program without let:

;; The let-to-lambda procedure (define (let-to-lambda expr) ...) ;; A list representing the let-to-lambda procedure (define let-to-lambda-code '(define (let-to-lambda expr) ...)) ;; A let-to-lambda procedure that does not use 'let'! (define let-to-lambda-without-let (let-to-lambda let-to-lambda-code))