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?
In Scheme, car , cdr , and cons are the most important functions. The cons function is used to construct pairs and pairs are used to construct the lists. The car and cdr are used to access data and return accordingly first and second element from a pair.
In Scheme, you can use local variables pretty much the way you do in most languages. When you enter a let expression, the let variables will be bound and initialized with values. When you exit the let expression, those bindings will disappear.
Like all programming languages, Scheme allows us to build our own procedures and add them to the set of existing ones. Very few implementations of Scheme even distinguish between the built-in functions and the user-defined ones.
Scheme procedures are first-class objects in the language; you refer to a procedure in the same way you refer to any other object, via a pointer. A "procedure name" is really just a variable name, and you can do the same things with "procedure" variables as with any other variable.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With