Question

(let ((+ *)
      (* +))
  (+ 3 (* 4 5)))

Output: 27

(let ((+ *))
  (+ 3 (* 4 5)))

Output: 60

I don't understand how let works. Can anyone help me?

Was it helpful?

Solution

let

let binds in parallel because

(let ((+ *) (* +))
  (+ 3 (* 4 5)))

actually expands to

((lambda (+ *) 
   (+ 3 (* 4 5))) 
 * +)

so Scheme does its usual thing:

  1. the parameters * and + are evaluated to their respective values (the procedures * and +) in some sequence
  2. then bound to the formal parameters + and * (this also happens in some sequence, but it doesn't matter which in both cases)
  3. then the body of the inner function is evaluated as (* 3 (+ 4 5)).

Just add a printf to confirm:

(let ((+ *) (* +))
  (printf "+ is ~a and * is ~a \n" + *)
  (+ 3 (* 4 5)))

=> 
+ is #<procedure:*> and * is #<procedure:+> 
27

let*

If you expect the binding to happen in sequence, then you have to use let* since

(let* ((+ *) (* +))
  (+ 3 (* 4 5)))

expands to

((lambda (+) 
   ((lambda (*) 
      (+ 3 (* 4 5))) 
    +))
 *)

so

  1. * is bound to +
  2. then + which has already be bound to *, is bound to * again so this actually has no effect
  3. then the expression becomes (* 3 (* 4 5))

and since the second binding has no effect this is equivalent to simply

(let ((+ *))
  (+ 3 (* 4 5)))

OTHER TIPS

In the first expression, * is bound to the original value of +, and + is bound to the original value of *. Thus (+ 3 (* 4 5)) would be equivalent to (* 3 (+ 4 5)) with the original bindings. That is the same as the infix expression 3 * (4 + 5), which is of course 27.

In the second expression, + is bound to the original value of * and * is unchanged. Thus (+ 3 (* 4 5)) would be equivalent to (* 3 (* 4 5)) with the original bindings, which is the same as the infix expression 3 * (4 * 5), which is 60.

The most important note here is that let does parallel binding. That means in an expression like (let ((+ *) (* +)) ...), the original values of * and + are taken first, and then + and * are bound simultaneously. That is why the new * has the old value of +, not its new value.

I recently wrote a post about the differences between the various let forms. Note the difference between let and let*, in particular.

Use the Stepper!

Racket has a nice stepper that can help answer this question for you. Paste your code into a buffer, change to the "Intermediate Student" Language, and click the "step" button. You'll see a highlighted sequence of steps.

Because this:

(let ((+ *)
      (* +))
  (+ 3 (* 4 5)))

is strictly equivalent to (this is called identifier renaming):

(let ((PLUS *)
      (TIMES +))
  (PLUS 3 (TIMES 4 5)))

because, as the other answers point out, the bindings are done in parallel. This means that the expression that is actually evaluated here is:

(* 3 (+ 4 5)) 

which is indeed 27.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top