tags:

views:

146

answers:

2

I've been studying Scheme recently and come across a function that is defined in the following way:

(define remove! 
    (let ((null? null?)
          (cdr cdr)
          (eq? eq?))
     (lambda ... function that uses null?, cdr, eq? ...)

What is the purpose of binding null? to null? or cdr to cdr, when these are built in functions that are available in a function definition without a let block?

+2  A: 

That's a speed optimization. Local variable access is usually faster than global variables.

Teddy
Can you explain in more detail? It seems that the process of binding with let would take just as much time as a lookup.
redwoolf
@redwoolf: The lookup when binding with `let` only happens once, but `cdr` might be used (and looked up) many, many times in the function.
Teddy
While this can have a speed benefit in some implementations, it is only a byproduct of the more important reason for doing this. I'll write about that in a separate answer.
Eli Barzilay
+13  A: 

In plain R5RS Scheme, there is no module system -- only the toplevel. Furthermore, the mentality is that everything can be modified, so you can "customize" the language any way you want. But without a module system this does not work well. For example, I write

(define (sub1 x) (- x 1))

in a library which you load -- and now you can redefine -:

(define - +) ; either this
(set! - +)   ; or this

and now you unintentionally broke my library which relied on sub1 decrementing its input by one, and as a result your windows go up when you drag them down, or whatever.

The only way around this, which is used by several libraries, is to "grab" the relevant definition of the subtraction function, before someone can modify it:

(define sub1 (let ((- -)) (lambda (x) (- x 1))))

Now things will work "more fine", since you cannot modify the meaning of my sub1 function by changing -. (Except... if you modify it before you load my library...)

Anyway, as a result of this (and if you know that the - is the original one when the library is loaded), some compilers will detect this and see that the - call is always going to be the actual subtraction function, and therefore they will inline calls to it (and inlining a call to - can eventually result in assembly code for subtracting two numbers, so this is a big speed boost). But like I said in the above comment, this is more coincidental to the actual reason above.

Finally, R6RS (and several scheme implementations before that) has fixed this and added a library system, so there's no use for this trick: the sub1 code is safe as long as other code in its library is not redefining - in some way, and the compiler can safely optimize code based on this. No need for clever tricks.

Eli Barzilay
thank you for the thorough response!
redwoolf