I now have learnt about arrays and aref
in Lisp. So far, it's quite easy to grasp, and it works like a charme:
(defparameter *foo* (make-array 5))
(aref *foo* 0) ; => nil
(setf (aref *foo* 0) 23)
(aref *foo* 0) ; => 23
What puzzles me is the aref
"magic" that happens when you combine aref
and setf
. It seems as if aref
knew about its calling context, and would then decide whether to return a value or a place that can be used by setf
.
Anyway, for the moment I just take this as granted, and don't think about the way this works internally too much.
But now I wanted to create a function that sets an element of the *foo*
array to a predefined value, but I don't want to hardcode the *foo*
array, instead I want to hand over a place:
(defun set-23 (place)
…)
So basically this function sets place to 23, whatever place is. My initial naive approach was
(defun set-23 (place)
(setf place 23))
and call it using:
(set-23 (aref *foo* 0))
This does not result in an error, but it also doesn't change *foo*
at all. My guess would be that the call to aref
resolves to nil
(as the array is currently empty), so this would mean that
(setf nil 23)
is run, but when I try this manually in the REPL, I get an error telling me that:
NIL is a constant, may not be used as a variable
(And this absolutely makes sense!)
So, finally I have two questions:
- What happens in my sample, and what does this not cause an error, and why doesn't it do anything?
- How could I solve this to make my
set-23
function work?
I also had the idea to use a thunk for this to defer execution of aref
, just like:
(defun set-23 (fn)
(setf (funcall fn) 23))
But this already runs into an error when I try to define this function, as Lisp now tells me:
(SETF FUNCALL) is only defined for functions of the form #'symbol.
Again, I wonder why this is. Why does using setf
in combination with funcall
apparently work for named functions, but not for lambdas, e.g.?
PS: In "Land of Lisp" (which I'm currently reading to learn about Lisp) it says:
In fact, the first argument in
setf
is a special sublanguage of Common Lisp, called a generalized reference. Not every Lisp command is allowed in a generalized reference, but you can still put in some pretty complicated stuff: […]
Well, I guess that this is the reason (or at least one of the reasons) here, why all this does not work as I'd expect it, but nevertheless I'm curious to learn more :-)
setf
is a macro. It's worth looking at its expansion. E.g., (on SBCL, as this is implementation dependent),(macroexpand '(setf (aref a 2) 42)) => (SB-KERNEL:%ASET A 2 42)
. – Chambertin