2

I'm currently writing metacircular evaluator in Scheme, following the SICP book's steps.

In the exercise I am asked to implement letrec, which I do in the following way:

(define (letrec->let exp)
  (define (make-unassigned var)
    (list var '*unassigned*))
  (define (make-assign binding)
    (list 'set! (letrec-binding-var binding)
          (letrec-binding-val binding)))
  (let ((orig-bindings (letrec-bindings exp)))
    (make-let
     (map make-unassigned
          (map letrec-binding-var orig-bindings))
     (sequence->exp
      (append
       (map make-assign orig-bindings)
       (letrec-body exp))))))

However, when I evaluate the expression as follows, it goes into infinite loop:

(letrec
  ((a (lambda () 1)))
  (+ 1 (a)))

Do I miss anything?

(Full Source Code at GitHub).

Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • did your code ever worked for the simplest cases like `(let ((x 1)) x)`? – Will Ness Mar 18 '14 at 09:13
  • this question is misleading. the problem in that code is at much deeper (earlier) level and has nothing to do specifically with letrec. – Will Ness Mar 18 '14 at 09:19

1 Answers1

1

I checked result transformation result of (let ((x 1)) x) and got:

((lambda (x) (x)) 1)

instead of:

((lambda (x) x) 1)

obviously problem is in let body processing. If I were you, I would use utility function:

(define (implicit-begin exps)
  (if (= 1 (length x))
      (car x)
      (cons 'begin x)))

By the way: I would say, that your letrec implementation is not very correct. Your transformation returns:

(let ((x1 *unassigned*>) ... (xn *unassigned*))
  (set! x1 ...)
  ...
  (set! xn ...)
  body)

It much more resembles letrec* which evaluates expressions for variable bindings in left-ro-right order (Scheme itself doesn't specify arguments evaluation order):

syntax: letrec* bindings body

 Similar to ‘letrec’, except the INIT expressions are bound to their
 variables in order.

 ‘letrec*’ thus relaxes the letrec restriction, in that later INIT
 expressions may refer to the values of previously bound variables.

The more correct code would be:

(let ((x1 *unassigned*) ... (xn *unassigned*))
  (let ((t1 ...) ... (tn ...))
    (set! x1 t1)
    ...
    (set! xn tn))
  body)
Victor Fedotov
  • 225
  • 2
  • 8
  • Do you mean to put the implicit-begin in my expand-let function so that it can has (car exps) in lambda body directly? – Cheng-En Chuang Mar 17 '14 at 16:29
  • the problem is that `make-lambda` assumes something, but `let-body` violates that assumption, in certain situations. – Will Ness Mar 18 '14 at 09:16