Tom
Tom

Reputation: 673

Ordering of nested let and lambda in Scheme

What is the difference between two functions in Scheme, one defined like this—

(define doSomething
    (lambda (x)
          (let (f (100))
               (f x))))

and the other like this?—

(define doSomething
          (let (f (100))
            (lambda (x) 
               (f x)))) 

In other words, what does it matter if the lambda is before the let or after it?

Upvotes: 3

Views: 1566

Answers (3)

okonomichiyaki
okonomichiyaki

Reputation: 8485

As Chris points out, the code isn't going to run. So I'm going to use a new example to explain the let over lambda idiom.

When you surround a lambda form with a let like this:

 (let ((x 0))
   (lambda ()
     ; body
     ))

The code inside the body of the lambda is able to access (including modify) x, even after the let ends and returns the new function. This is one example of creating a "closure" which is tricky to understand at first.

Essentially this means that you can create a function with a kind of "internal state". You can use this to do things like make "accumulator generators" or make functions that count the number of times they've been called, or even simulate "objects" (internal state + methods). Here's some contrived examples:

A double function that counts the number of times it's been called:

(define double
  (let ((count 0)) ; closed over variable
    (lambda (x)
      (if (eq? x 'count)
          count
          (begin 
            (set! count (+ count 1)) ; incr the count variable introduced by let
            (+ x x))))))

> (double 1)
2
> (double 1)
2
> (double 1)
2
> (double 'count) ; ask the double function how many times it's been called
3
> 

This example is really courtesy Paul Graham (http://www.paulgraham.com/accgen.html)

(define make-accumulator
  (lambda ()
    (let ((x 0))
      (lambda (i)
        (set! x (+ x i)) ; incr x by i
        x))))

> (define acc (make-accumulator))
> (acc 1)
1
> (acc 1)
2
> (acc 1)
3
> (acc 1)
4
> 

Each time acc is called with 1 the value it returns is different.

For examples of "objects" search for "objects and closures" or just read the relevant sections of SICP: http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-20.html#%_sec_3.1

Upvotes: 5

John Clements
John Clements

Reputation: 17203

One point that the other two (excellent) posters circle around but don't explicitly mention is this: in the absence of set!, there's no difference between the two, and therefore probably no reason to use your second form.

Upvotes: 2

C. K. Young
C. K. Young

Reputation: 222973

Your code won't run. :-)

I will presume that you mean these instead:

(lambda (x)
  (let ((f 100))
    (+ f x)))

and

(let ((f 100))
  (lambda (x)
    (+ f x)))

In both cases, you'll get back the passed-in argument plus 100.

However, the main difference (ignoring technicalities about how let is just syntactic sugar over lambda) is that in the second version, f is a free variable. Say we do this:

(let ((f 100))
  (list (lambda (x)
          (+ f x))
        (lambda (x)
          (set! f x))))

This returns a list with two lambdas: the first of which is just like the ones previously, and the second one which allows you to change the value of f. Both lambdas access the same f, so running the setter will affect later calls to the first lambda.

Upvotes: 5

Related Questions