8

So as far as I understand, the following: let, let*, letrec and letrec* are synthetics sugars used in Scheme/Racket.

Now, if I have a simple program:

(let ((x 1)
      (y 2))
     (+ x y))

It is translated into:

((lambda (x y) (+ x y)) 1 2)

If I have:

(let* ((x 1)
      (y 2))
     (+ x y))

It is translated into:

((lambda (x) ((lambda (y) (+ x y))) 2) 1)

Now, for my first question, I understand the meaning of a letrec expression, which enables one to use recursion inside a let, but I do not understand how exactly it is done. What is letrec translated to?

For example, what will

(letrec ((x 1)
      (y 2))
     (+ x y)) 

be translated into?

The second question is similar about letrec* - But for letrec* I do not understand how exactly it differs from letrec? And also, what will a letrec* expression be translated into?

  • 1
    `letrec` is not translated into anything. `letrec` is a primitive form of the language. (And, for what it’s worth, in Racket, `let` is *also* a primitive form in Racket, not a macro on top of `lambda`, but that’s much more of a subjective design choice.) – Alexis King May 31 '18 at 15:29
  • Whether or not its translated is one thing. But can't there be an equivalent expression for `letrec`, the same way there is an equivalent expression for `let` and `let*` (such as I specified in my examples)? –  May 31 '18 at 15:32
  • 1
    see e.g. [this](https://stackoverflow.com/questions/7719004/in-scheme-how-do-you-use-lambda-to-create-a-recursive-function/11833038#11833038) and [this](https://stackoverflow.com/questions/15003518/confused-by-the-difference-between-let-and-let-in-scheme/15006018#15006018) . (disclaimer: both are my answers). the last one actually has two possible expansions, for the `letrec` and `letrec*`. (so, this might even be a duplicate...) – Will Ness May 31 '18 at 15:56

2 Answers2

5

See the paper "Fixing Letrec: A Faithful Yet Efficient Implementation of Scheme’s Recursive Binding Construct" by Oscar Waddell, Dipanwita Sarkar, and, R. Kent Dybvig.

The paper starts with a simple version and proceeds to explain a more sophisticated expansion:

https://www.cs.indiana.edu/~dyb/pubs/fixing-letrec.pdf

soegaard
  • 28,660
  • 4
  • 50
  • 97
3

Since you example does not have any procedures and no real expressions I'd say you could implement it as:

(let ((x 1) (y 2))
  (+ x y)) 

But to support the intention of the form a basic implementaiton might do something like this:

(let ((x 'undefined) (y 'undefined))
  (let ((tmpx 1) (tmpy 2))
    (set! x tmpx)
    (set! y tmpy))
  (+ x y))

Now. letrec is for lambdas to be able to call themselves by the name. So imagine this:

(let ((fib (lambda (n) 
             (if (< n 2) n 
                 (+ (fib (- n 1)) (fib (- n 2)))))))
      (fib 10))

It's important to understand that this does not work and why. Transforming it into a lambda call makes it so easy to see:

((lambda (fib)
   (fib 10))
 (lambda (n) 
   (if (< n 2) n 
       (+ (fib (- n 1)) (fib (- n 2))))))

Somehow you need to evaluate the lambda after fib is created. Lets imagine we do this:

(let ((fib 'undefined))
  (set! fib (lambda (n) 
              (if (< n 2) n 
                  (+ (fib (- n 1)) (fib (- n 2))))))
  (fib 10))

Alternatively you can do it with a Z combinator to make it pure:

(let ((fib (Z (lambda (fib)
                (lambda (n) 
                  (if (< n 2) n 
                      (+ (fib (- n 1)) (fib (- n 2)))))))))  
  (fib 10))

This requires more work by the implementation, but at least you do without mutation. To make it work with several mutual recursive bindings probably takes some more work but I'm convinced its doable.

These are the only two ways to do this properly. I know clojure has a recur which imitates recursion but in reality is a a goto.

For variables that do not make closures from the letrec bindings they work as let and later more like let* since Soegaards answer about fix is backwards compatible and some has adapted it. If you write compatible code though you should not be tempted to assume this.

Sylwester
  • 44,544
  • 4
  • 42
  • 70