Discussion of the C code
sds's answer address the main points of the question, but it does look like there's a little bit of confusion about what happens in the C code that you're emulating:
I would like to know if there is any way to mimic C behaviour with
pointers in LISP. In C if you change a value of a variable, that
pointer is pointing to, it has a global effect (i.e. the value will be
changed outside the function too).
Consider the following, which I think is most closely analogous to the Lisp code that you provided:
#include<stdio.h>
int a = 3;
int mutate( int a ) {
return a = 5;
}
int main() {
mutate( a ); /* or mutate( 8 ) or anything other argument */
printf( "%d\n", a ); /* prints 3 */
return 0;
}
The code prints three because the a
in mutate
is a variable that only exists within mutate
. Just because it shares a name with the global a
doesn't mean that changing one will change the other. The only place in this code that you can change the value of mutate
's variable a
is in mutate
. You don't have the option of “changing [the] value of a variable that [a] pointer is pointing to”. What you can do is pass the pointer to the value of a variable, modify the value through that pointer, and then observe the results in the value afterward. This would correspond to this C code:
#include<stdio.h>
int a = 3;
int mutate( int *a ) {
return (*a = 5);
}
int main() {
mutate( &a );
printf( "%d\n", a ); /* prints 5 */
return 0;
}
Indirection through structures
You can do things like this in Common Lisp, too, using any kind of indirection you like. For instance, if you make a
a cons
cell whose car
is 3
, then you can pass that cons
around and modify the value of its car
:
CL-USER> (defparameter *a* (cons 3 nil))
*A*
CL-USER> (defun mutate (cons)
(setf (car cons) 5))
MUTATE
CL-USER> (mutate *a*)
5
CL-USER> (car *a*)
5
You don't have an address-of operator in Lisp, though, so you can't do the exactly analogue of the C code, and you'd always need to “wrap” the value somehow, if you want to use this approach. You can use the existing structures within Common Lisp such as cons cells, vectors, or anything else you can find.
Generalized References
Although it doesn't have C-style pointers, Common Lisp defines a very broad way of referring to memory locations for reading and writing, called Generalized Reference.
A generalized reference is the use of a form, sometimes called a
place, as if it were a variable that could be read and written. The
value of a place is the object to which the place form evaluates. The
value of a place can be changed by using setf. The concept of binding
a place is not defined in Common Lisp, but an implementation is
permitted to extend the language by defining this concept.
In Common Lisp, you can assign to places using setf
. The suggestions that sds gave share in common that you can modify the value of a global variable either by using the global variable symbol as a place for setf
, or with symbol-value
. That is, after a definition such as (defparameter *a* 3)
both *a*
and (symbol-value '*a*)
are places in which you can store a new value for *a*
. As a result, I'd rather write a macro with variable names place
and value
, so that it's clear that any place can be used as an argument:
(defmacro mutate (place value)
`(setf ,place ,value))
Simulating C-style pointers to variables using lexical closures
Because lexical variables are also places, there's another option that hasn't yet been considered. You can use lexical closures to create functions that will give you the same kind of capabilities as C-style pointers.
(defmacro make-pointer (place)
`(lambda (op &optional value)
(ecase op
((read) ,place)
((write) (setf ,place value)))))
(let* ((x 3)
(xp (make-pointer x)))
(funcall xp 'write 5) ; write a new value to x
(list (funcall xp 'read) ; read the value from x through xp
x)) ; read the value from x directly
;=> (5 5)
In this code, make-pointer
returns a function that can be called with one or two arguments. The first argument should be a symbol, either read
or write
, and the second argument, which should provided when the first is write
, is a new value to store in the place. When called with read
, the value of the place is returned. When called with write
, a new value is stored and returned.
There are some issues with multiple evaluation here, though. For instance, if you were to do the following, remembering that (print 2)
returns the value 2
:
(make-pointer (aref some-array (print 2)))
you'll end up printing 2
every time that you read or write using the pointer, which is probably not desired. I don't know whether this needs to be addressed for this question or not, but keep reading for some possible ways to avoid this.
After some research about a similar question (How to mutate global variable passed to and mutated inside function?), it's worth noting that the Lisp Machines (which ran Lisp Machine Lisp, not Common Lisp), had a concept more like C-pointers called locatives, which are briefly mentioned in the answer to Common Lisp, reference to value and actual value. Once you know the term to search for, it's easy to find out more about locatives, including Chapter 13. Locatives from the Lisp Machine Manual and various reïmplementations for Common Lisp, including Alan Crowe's which begins with a long comment that ends with a (promising) concise summary:
;;; The basic idea is to use closures
Later on (the source reads very nicely), you'll get to:
;;; It looks as though we are done
;;; now we can translate C code
;;; &x = (addr x), *x = (data x)
but there's a caveat
;;; The trouble is, we have a multiple evaluation bug.
Crowe goes on to show how get-setf-expansion
can be used to create the functions that remember how to access the location and store values to it without needing to evaluate (print 2)
each time. That code is certainly worth a read!