Error
In your code, you write:
(defun play (&rest args)
(format #.*standard-output* "play ~A~{, ~A~}~%" (car args) (cdr args))))
Before the code is evaluated, it is first read: the source is turned into an abstract syntax tree, which is represented as lisp values. Upon encountering #.*standard-output*
, the reader evaluates the next form, here *standard-output*
: the actual stream bound to this variable when you read the form is stored in the structured-expression that is the source code of function play
.
You can put arbitrary data in your code, notably strings, numbers, etc. Here your source code contains an instance of a stream. This could be a problem when you try to execute function play
, because by the time you evaluate the function’s body, the stream might be closed; it looks like you are trying to fix the stream to which you want the output to be written, but that’s probably not a good way to do what you have in mind. If you edit your question to add more information about that, we can probably find better ways to solve your problem.
Storing the stream object is also a problem when you compile the code, which is probably the reason why you have this particular error here.
As noted in comments, this happens when you compile a file. If you are working with Slime, this may happen when the environment uses a temporary file to compile buffer regions.
So your error happen when using COMPILE-FILE
; note in particular this paragraph in the spec:
Programs to be compiled by the file compiler must only contain externalizable objects; for details on such objects, see Section 3.2.4 (Literal Objects in Compiled Files). For information on how to extend the set of externalizable objects, see the function make-load-form and Section 3.2.4.4 (Additional Constraints on Externalizable Objects).
The file compiler produces an object file (fasl extension). Some literal values like constant strings, etc. must be stored in the resulting file, in a way that they similar objects can be reconstructed when loading the file back (ยง3.2.4 Literal Objects in Compiled Files). For most standard types, the Lisp compiler knows how to dump an object and load it back.
But here the compiler does not know how to dump a value of type stream
during the compilation process. You could define methods for MAKE-LOAD-FORM
but some data are inherently hard to (de)serialize: how do we store threads or any other operating system resource?
Format
Except from evaluating *standard-output*
at read-time, the format works.
However, you decompose the list into its car
and cdr
, but:
you don’t check if there are any element in the list; if
args
is NIL then the printed text is"play NIL"
, which is ambiguous with a list of one element NIL; or maybe you only ever call the function with non-empty lists, and in that case you probably should add(check-type list cons)
to better catch programming errors. Alternatively, defineplay
as follows:(defun play (arg &rest more-args) ...)
Then,
ARG
is the car, andMORE-ARGS
is the cdr, and the user cannot call the function with an empty list of args (the constraint is also more explicit in the function signature).you decompose them to handle the addition of comma between elements, which is something that
format
can handle by itself already.(format t "~{~a~^, ~}" args)
The
~^
caret operator exits the current iteration context in format if there are no remaining elements in the list being processed. So here, we only add a comma and a space when the are more elements in the list. Also, you may want to handle the empty list and print something else, likenot playing anything
in case the list is empty; in that case you use the following construct:"~:[ ... ~; ... ~]"
The bracket operator with a colon modifier is like an if, the part before
~;
is for the NIL case, the part that follows~;
is for the non-nil part; you would need to write something like this:(format t "~:[not playing anything~;play ~{~a~^, ~}~]" args args)
Notice how
args
is supplied twice: first for the test, then it is consumed (only) in the true branch of the test. We can do a bit better by letting format rewind on its list of parameters, so that it can consume a single parameterargs
twice:(format t "~:[not playing anything~;~:*play ~{~a~^, ~}~]" args)
The asterisk operator with colon modifier
~:*
changes which current parameter is being consumed byformat
, by going back one in the list of supplied argument.
You can read more about format here: A Few FORMAT Recipes
CLICK HERE to find out more related problems solutions.