lambdaschemelet

translating a let as lambda


From page 69 of R⁷RS I know what I can substitute a let with a lambda:

(let (a 10) something)
; is equivalent to
(lambda (a) something) 10

So I took this (from SICP):

(define new-withdraw
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

and translated it to:

(define w-lam
  (lambda (balance)
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds")) 100))

but the behaviour in the REPL is different:

(new-withdraw 80)

;Value: 20

(w-lam 80)

;Value: 100

What am I missing?


Solution

  • Equivalent of

    (let ((a 10)) something)
    ; With 2 pairs of parenthesis, but I take that is a typo
    

    isn't

    (lambda (a) something) 10
    

    But

    ((lambda (a) something) 10)
    

    (lambda (a) something) 10 isn't a valid construct anyway. It is 2, unrelated valid constructs (and, depending on the context, it may be legal or not to have two constructs one after the other). It is first a function, that does something in a context where a is a parameter. And then, a number, 10. You don't want a function that evaluate something. You want to actually evaluate something. So you need to call that function.

    (lambda (a) something) 10 is like print; 10; in an imperative language. It means nothing. It is just a function (print) and then a number. Without connections. It happens to be legal, but void, in many languages. The function is not called. It is just a value (that happens to be a function). Which is ignored, since we do nothing with it.

    ((lambda (a) something) 10) on the other hand calls that function with parameter 10. Like print(10); in my analogy.

    So, equivalent of

    (let ((balance 100))
        (lambda (amount)
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds")))
    

    is

    
    ((lambda (balance) 
        (lambda (amount)
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds"))) 100)
    

    tl-dr: here is the working code

    Or, enclosed in your define w-lam

    (define w-lam
      ((lambda (balance) 
        (lambda (amount)
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds"))) 100))
    

    Note that the equivalent let/lambda isn't just black magic. You don't need to find "formulas" in textbook for that. It is just logic.

    (let ((a 10)) something)
    

    Means that we create a new environment, in which symbol a is associated to value 10. And in this environment, something is evaluated

    (lambda (a) something)
    

    is a function that evaluate something, in a local environment in which a is the parameter passed to that function. And, as you know

    (f 10)
    

    Calls function f with parameter 10.

    So, just replace f by previous function (lambda (a) something). And you get

    ((lambda (a) something) 10)
    

    Which calls (lambda (a) something) with 1st parameter being 10. And we know that calling this function means evaluating something in an environment where a is the 1st parameter passed, that is, here, 10.

    If you are more familiar with more imperative languages, it just a replacing

    { // New block
        int a=10;
        printf("%d\n", a)
    }
    

    By

    void f(int a){
        printf("%d\n", a);
    }
    f(10);
    

    Or, in javascript (which has closer to scheme constructs)

    {
        let a=10;
        console.log(a);
    }
    

    by

    (function (a){ 
        console.log(a);
    })(10);
    

    Or

    ((a)=>console.log(a))(10)
    

    Which is something people really do in javascript to create local environments.

    So, it is just using parameters of a lambda (or anonymous functions in my analogies) as a local environment, and calling that function immediately with values we want to set in this local environment.