I am reading the book Lisp written by Winston. In addition, I am using SBCL, Emacs, and Slime.
In chapter 8 (about macros), the book has the following exercise:
Problem 8-7: A stack is a learnly ordered set of things that can be accessed using push and pop operations. OUR-PUSH adds a new item to the top of the stack, while OUR-POP removes the item on top of the stack. A list can be used to represent a stack, with the first element corresponding to the item on top. Define OUR-PUSH and OUR-POP as macros. OUR-PUSH takes two arguments, the item to be pushed and the name of a variable whose value it the list representing the stack. The value returned is the enlarged list. OUR-POP takes a single element, the name of the variable whose value is in the list. The value returned is the item popped. In both cases the value of the variable is changed to reflect the new state of the stack.
I got it correct for the pop
function. However, I had a trouble with the push
re-implementation. This is the answer-sheet:
(defmacro book-our-push (item-to-be-pushed stack)
`(setq ,stack (cons ,item-to-be-pushed ,stack)))
It works as expected. My initial answer was similar:
(defmacro our-push (item-to-be-pushed stack)
`(setf ,stack (list ,item-to-be-pushed ,stack)))
But this generates an accidental list nesting such as:
CL-USER> (defparameter so-stack '(3 4 5 6))
SO-STACK
CL-USER> so-stack
(3 4 5 6)
CL-USER> (our-push 2 so-stack)
(2 (3 4 5 6))
Then, I thought: "Oh, this must the kind of situation in which you use ,@
". Because ,@
creates a splicing behavior. Thus, I did:
(defmacro our-push (item-to-be-pushed stack)
`(setf ,stack (list ,item-to-be-pushed ,@stack)))
However, it does not work. Moreover, Slime throws an error message that feels weird to me:
The value
SO-STACK
is not of type
LIST
Specially because:
CL-USER> (listp so-stack)
T
Is there a way to use ,@
for this situation?
Why is slime indicating the variable so-stack
is not a list when it actually is one?
You are at the worng level. When the macro is expanded using its macro function, you are operating on the source code.
The source code is this list:
(my-push thing foos)
i. e. a list with three symbols, named my-push
, thing
, and foos
.
The macro function takes this list and transforms it to a different list, in your case:
(setf foos (list thing foos))
This is then compiled further (more macro expansions, finally compilation to machine code).
If you try to splice foos
that's impossible, because a symbol is not a list.
The macro function never sees what these symbols mean. It is finished before the form even gets compiled to machine code.