Go to the first, previous, next, last section, table of contents.

All Values are Pointers

Conceptually, all Scheme objects are allocated on the heap, and referred to via pointers. This uniformity actually makes life simple, because you don't have to worry about whether you should dereference a pointer when you want to use a value--you always do. Since pointer dereferencing is uniform, procedures always dereference a pointer to a value when they really use the value, and you never have to explicitly force the dereferencing.

For example, the predefined Scheme procedure + takes two pointers to numbers, and automatically dereferences both pointers before doing the addition. It returns a pointer to the number that's the result of the addition.

So when we evaluate the expression (+ 2 3) to add two to three, we are taking a pointer to the integer 2 and a pointer to integer 3, and passing those as arguments to the procedure +. + returns a pointer to the integer 5. We can nest expressions, e.g., (* (+ 2 3) 6), so that the pointer to five is passed, in turn, to the procedure *. Since these functions all accept pointers as arguments and return pointers as values, you can just ignore the pointers, and write arithmetic expressions the way you would in any other language.

When you think about it, it doesn't make any sense to change the value of an integer, in a mathematical sense. For example, what would it mean to change the integer 6's value to be 7? It wouldn't mean anything sensible, for sure. 6 is a unique, abstract mathematical object that doesn't have any state that can be changed---6 is 6, and behaves like 6, forever.

What's going on in conventional programming languages is not really changing the value of an integer--it's replacing one (copy of an) integer value with (a copy of) another. That's because most programming languages have both pointer semantics (for pointer variables) and value semantics (for nonpointer variables, like integers). You make multiple copies of values, and then clobber the copies when you perform an assignment.

In Scheme, we don't need to clobber the value of an integer, because we get the effect we want by replacing pointers with other pointers. An integer in Scheme is a unique entity, just as it is in mathematics. We don't have multiple copies of a particular number, just multiple references to it. (Actually, Scheme's treatment of numbers is not quite this simple and pretty, for efficiency reasons we'll explain later, but it's close.)

As we'll see later, an implementation is free to optimize away these pointers if it doesn't affect the programmer's view of things--but when you're trying to understand a program, you should always think of values as pointers to objects.

The uniform use of pointers makes lots of things simpler. In C or Pascal, you have to be careful whether you're dealing with a raw value or a pointer. If you have a pointer and you need the actual value, you have to explicitly dereference the pointer (e.g., with C's prefix operator *, or Pascal's postfix operator ^). If you have a value and you need a pointer to it, you have to take its address (e.g., with C's prefix & operator).

In Scheme, none of that mess is necessary. User-defined routines pass pointers around, consistently, and when they bottom out into predefined routines (like the built-in + procedure or set! special form) those low-level built-in operations do any dereferencing that's necessary.

(Of course, when traversing lists and the like, the programmer has to ask for pointers to be dereferenced, but from the programmer's point of view, that just means grabbing another pointer value out of a field of an object you already have a pointer to.)

It is sometimes said that languages like Scheme (and Lisp, Smalltalk, Eiffel, and Java) "don't have pointers." It's at least as reasonable to say that the opposite is true--everything's a pointer. What they don't have is a distinction between pointers and nonpointers that you have to worry about.(2)


Go to the first, previous, next, last section, table of contents.