14
votes

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

2 Answers

30
votes

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.

2
votes

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