4.8 Exercise 4.8

In order to support the named let form, we can check whether the second element of the let expression is a list or not. If it is a list, then we are using a standard let expression. If it is a variable, then we have a named let.

First, we define a predicate for determining if an expression (which is already assumed to be a let expression of some form) is a named let expression, and write new accessors for it:

(define (named-let? exp) (variable? (cadr exp)))
(define (named-let-name exp) (cadr exp))
(define (named-let-bindings exp) (caddr exp))
(define (named-let-parameters exp) (map car (named-let-bindings exp)))
(define (named-let-initial-values exp) (map cadr (named-let-bindings exp)))
(define (named-let-body exp) (cdddr exp))

Next we define the new version of let->combination, which chooses to reduce a named let expression to a define (since we need to call this procedure recursively, it needs a name, and this is how we know how to do this). I’ve also defined a make-define procedure in line with the other make- procedures. Note that this define is wrapped inside a let with no bindings – this is required for the expression to be legal.

(define (make-define binding val) (list 'define binding val))
(define (let->combination exp)
  (if (named-let? exp)
      (make-let
       '()
       (make-define
        (named-let-name exp)
        (make-lambda (named-let-parameters exp) (named-let-body exp)))
       (cons (named-let-name exp) (named-let-initial-values exp)))
      (list (make-lambda (let-names exp) (let-body exp))
            (let-values exp))))