Why can CLOS slots be unbound? - lisp

It is said, only special variables in Common Lisp can be unbound. For all lexical variables the default value is nil. I thought that class slots exist in something like closure, but obviously they don't.
If I define a CLOS slots without :initform parameter (in hope that they will be bound to nil anyway) and do not supply values when creating instance, I'll get an instance with unbound slots. Why it is so? It's not handy.

It is very handy for things like computing slot values on demand, where the value may sometimes be nil. When accessing an unbound slot, CLOS normally calls the SLOT-UNBOUND generic function, which signals an error. Instead of an error, though, you can specialize SLOT-UNBOUND to compute and store the value on demand. Subsequent accesses will use the slot value directly, and you can flush the slot "cache" by using SLOT-MAKUNBOUND.
You could do this with a sentinel "not-bound" value of some sort, but it's very handy to have the functionality built-in instead.
Example of slot-unbound use:
(defclass foo ()
((bar :accessor bar)
(baz :accessor baz)))
(defmethod slot-unbound (class (instance foo) slot-name)
(declare (ignorable class))
(setf (slot-value instance slot-name) nil))
In action:
CL-USER> (defparameter *foo* (make-instance 'foo))
*FOO*
CL-USER> (bar *foo*)
NIL
CL-USER> (setf (baz *foo*) (not (baz *foo*)))
T

CLOS instances are not closures
CLOS instances are usually not implemented as closures. That would be difficult. They are some data structure with something like a vector for the slots. Similar to Common Lisp's structures. A difference between CLOS instances and structures complicates it: it is possible for CLOS instances to change the number of slots at runtime and one can change the class of a CLOS instance at runtime.
Making sure slots have a NIL value
With some advanced CLOS you can make sure that slots have a NIL value. Note that the functions in the package CLOS in my example may be in another package in your CL.
This functions looks over all slots of an instance. If a slot is unbound, it gets set to NIL.
(defun set-all-unbound-slots (instance &optional (value nil))
(let ((class (class-of instance)))
(clos:finalize-inheritance class)
(loop for slot in (clos:class-slots class)
for name = (clos:slot-definition-name slot)
unless (slot-boundp instance name)
do (setf (slot-value instance name) value))
instance))
We make a mixin class:
(defclass set-unbound-slots-mixin () ())
The fix will be run after initializing the object.
(defmethod initialize-instance :after ((i set-unbound-slots-mixin) &rest initargs)
(set-all-unbound-slots i nil))
Example:
(defclass c1 (set-unbound-slots-mixin)
((a :initform 'something)
b
c))
CL-USER 1 > (describe (make-instance 'c1))
#<C1 4020092AEB> is a C1
A SOMETHING
B NIL
C NIL

Related

defstruct - :read-only is not read only

In the CLHS I read for :read-only x: "When x is true, this specifies that this slot cannot be altered; it will always contain the value supplied at construction time."
Bit I can do this (CCL, SBCL):
CL-USER> (defstruct foo
(one 0 :read-only t))
FOO
CL-USER> (defparameter *foo* (make-foo))
*FOO*
CL-USER> *foo*
#S(FOO :ONE 0)
CL-USER> (setf (slot-value *foo* 'one) 1)
1 (1 bit, #x1, #o1, #b1)
CL-USER> *foo*
#S(FOO :ONE 1)
Shouldn't changing this slot be prohibited by Lisp?
slot-value is not how you access the fields of an object whose class was defined with defstruct. Such objects do not portably have named slots at all: they have named accessors.
Some implementations give the fields of such objects names, and may also allow access to them with slot-value: such behaviour is entirely nonportable however.
If you work within the language defined by the standard then you should not be able to modify the value of a structure field defined with a :read-only option.
The specification says:
setf will not accept the reader function for this slot.
slot-value is not the reader function created by defstruct. The reader function is foo-one (unless you override the naming scheme with the :conc-name keyword). So you should get an error if you try to do
(setf (foo-one *foo) 1)

Why is my variable "undefined"?

Why is my variable nodes undefined in the vector-push-extend line?
(defun make_graph (strings)
(defparameter nodes (make-array 0))
(loop for x in strings do
(vector-push-extend (make-instance 'node :data x) nodes))
n)
The short answer is that you should use let instead of defparameter to introduce your variable. For instance:
(defun make_graph (strings)
(let ((nodes (make-array 0)))
(loop for x in strings do
(vector-push-extend (make-instance 'node :data x) nodes))
;; your code says N here, but I assume that's a typo...
nodes))
The defparameter form is useful for creating "special" variables, which are somewhat similar to global variables in other programming languages. (There are some differences, e.g., the special variables introduced by defparameter aren't exactly global---instead, they are dynamically scoped, and can be let bound, etc...)
At any rate, the let form will instead create a local variable.
DEFPARAMETER is used at toplevel to define global special variables.
Toplevel:
(defparameter *foo* 42)
Still at toplevel, because forms inside PROGN are still at toplevel (by definition):
(progn
(defparameter *foo* 42)
(defparameter *bar* 32))
Not at toplevel:
(defun baz ()
(defparameter *foo* 42))
Above last form is not recognized by the compiler as a variable declaration. But when one calls (baz) and the function is running, the variable is defined and initialized.
A non-toplevel use of DEFPARAMETER will not be recognized by the compiler, but at runtime it will create a special global variable.
(defun make_graph (strings)
(defparameter nodes (make-array 0))
(loop for x in strings do
(vector-push-extend (make-instance 'node :data x) nodes))
n)
The compiler warns:
;;;*** Warning in MAKE_GRAPH: NODES assumed special
;;;*** Warning in MAKE_GRAPH: N assumed special
Thus in above code, the compiler does not recognize nodes as a defined variable, if it wasn't defined somewhere else already. The use of nodes in the function creates a warning.
Still the code might work, since at runtime the variable is created and initialized - but for every function invocation. Over and over. This compiler also assumes that nodes is just this: some kind of special variable. Still I would not count on it for all compilers.
n is also not defined anywhere.
Notes:
the correct way to introduce local lexical variables is to use LET and LET* (and other binding forms)
use DEFPARAMETER as a toplevel form. It is unusual when it's not a toplevel form. Typically the author makes a mistake then.

Trying to understand setf + aref "magic"

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 :-)
A place is nothing physical, it's just a concept for anything where we can get/set a value. So a place in general can't be returned or passed. Lisp developers wanted a way to easily guess a setter from just knowing what the getter is. So we write the getter, with a surrounding setf form and Lisp figures out how to set something:
(slot-value vehicle 'speed) ; gets the speed
(setf (slot-value vehicle 'speed) 100) ; sets the speed
Without SETF we would need a setter function with its name:
(set-slot-value vehicle 'speed 100) ; sets the speed
For setting an array we would need another function name:
(set-aref 3d-board 100 100 100 'foo) ; sets the board at 100/100/100
Note that the above setter functions might exist internally. But you don't need to know them with setf.
Result: we end up with a multitude of different setter function names.
The SETF mechanism replaces ALL of them with one common syntax. You know the getter call? Then you know the setter, too. It's just setf around the getter call plus the new value.
Another example
world-time ; may return the world time
(setf world-time (get-current-time)) ; sets the world time
And so on...
Note also that only macros deal with setting places: setf, push, pushnew, remf, ... Only with those you can set a place.
(defun set-23 (place)
(setf place 23))
Above can be written, but place is just a variable name. You can't pass a place. Let's rename it, which does not change a thing, but reduces confusion:
(defun set-23 (foo)
(setf foo 23))
Here foo is a local variable. A local variable is a place. Something we can set. So we can use setf to set the local value of the variable. We don't set something that gets passed in, we set the variable itself.
(defmethod set-24 ((vehicle audi-vehicle))
(setf (vehicle-speed vehicle) 100))
In above method, vehicle is a variable and it is bound to an object of class audi-vehicle. To set the speed of it, we use setf to call the writer method.
Where does Lisp know the writer from? For example a class declaration generates one:
(defclass audi-vehicle ()
((speed :accessor vehicle-speed)))
The :accessor vehicle-speed declaration causes both reading and setting functions to be generated.
The setf macro looks at macro expansion time for the registered setter. That's all. All setf operations look similar, but Lisp underneath knows how to set things.
Here are some examples for SETF uses, expanded:
Setting an array item at an index:
CL-USER 86 > (pprint (macroexpand-1 '(setf (aref a1 10) 'foo)))
(LET* ((#:G10336875 A1) (#:G10336876 10) (#:|Store-Var-10336874| 'FOO))
(SETF::\"COMMON-LISP\"\ \"AREF\" #:|Store-Var-10336874|
#:G10336875
#:G10336876))
Setting a variable:
CL-USER 87 > (pprint (macroexpand-1 '(setf a 'foo)))
(LET* ((#:|Store-Var-10336877| 'FOO))
(SETQ A #:|Store-Var-10336877|))
Setting a CLOS slot:
CL-USER 88 > (pprint (macroexpand-1 '(setf (slot-value o1 'bar) 'foo)))
(CLOS::SET-SLOT-VALUE O1 'BAR 'FOO)
Setting the first element of a list:
CL-USER 89 > (pprint (macroexpand-1 '(setf (car some-list) 'foo)))
(SYSTEM::%RPLACA SOME-LIST 'FOO)
As you can see it uses a lot of internal code in the expansion. The user just writes a SETF form and Lisp figures out what code would actually do the thing.
Since you can write your own setter, only your imagination limits the things you might want to put under this common syntax:
setting a value on another machine via some network protocol
setting some value in a custom data structure you've just invented
setting a value in a database
In your example:
(defun set-23 (place)
(setf place 23))
you can't do it just like that, because you have to use setf in context.
This will work:
(defmacro set-23 (place)
`(setf ,place 23))
CL-USER> (set-23 (aref *foo* 0))
23
CL-USER> *foo*
#(23 NIL NIL NIL NIL)
The trick is, setf 'knows' how to look at real place its arguments come from, only for limited number of functions. These functions are called setfable.
setf is a macro, and to use it the way you wanted to, you also have to use macros.
The reason why you have not been getting errors, is that you actually successfully modified lexical variable place which was bound to copy of selected array element.

"Overloading" CLOS multimethods With Different Parameter Lists

I am trying to do an "overload call" of a multimethod in Common Lisp. Here is a simplified rundown of the case:
(defclass foo ()
((slotty :accessor slotty :initarg :slotty)))
(defclass bar ()
((slotty :accessor slotty :initarg :slotty)))
(defparameter *foo* (make-instance 'foo :slotty "defnoodle"))
(defparameter *bar* (make-instance 'bar :slotty "Chocolate"))
(defmethod contrived ((f foo) (b bar))
(format t "i pity the foo ~A, who has a bar ~A ~%" (slotty f) (slotty b)))
(contrived *foo* *bar*)
outputs: i pity the foo defnoodle, who has a bar Chocolate
But as soon as I try to define the next method:
(defmethod contrived ((f foo))
(format t "i just pity the foo ~A ~%" (slotty f)))
CL gets mad:
; The generic function #<STANDARD-GENERIC-FUNCTION CONTRIVED (1)>
; takes 2 required arguments; was asked to find a method with
; specializers (#<STANDARD-CLASS FOO>)
; [Condition of type SB-PCL::FIND-METHOD-LENGTH-MISMATCH]
; See also:
; Common Lisp Hyperspec, FIND-METHOD [:function]
Does anyone know what I am doing wrong here? I know that initialize-instance has a similar kind of flexibility since one should be able to identify n number of initialize-instance methods per class and per any number of arguments.
(defmethod initialize-instance :after ((f foo) &key)
())
but it is not clear to me how I can translate this to the vanilla example I gave above. And I feel like I could be barking up the wrong tree since this is part of MOP.
You write: Does anyone know what I am doing wrong here?
To make it clear: CLOS does not support that. You can't have different numbers of required arguments in method parameter lists of methods of a single generic function. Dispatching works over the required arguments only. Common Lisp does not support 'overloading'.
INITIALIZE-INSTANCE is defined with this syntax:
initialize-instance instance &rest initargs &key &allow-other-keys => instance
All methods take one required argument, the instance. Dispatching is only done over this object. It then allows various keyword arguments - no dispatching is done for them.
So you need to agree on the numbers of required arguments your generic function should take and call it that way in your code.
See the CL Hyperspec for the rules: Congruent Lambda-lists for all Methods of a Generic Function.
All methods of a generic function must have a congruent argument list. Argument lists are congruent if you have the same number of required arguments, the same number of optional arguments, and use &rest and &key in a compatible way.
But, of course, you cannot specialize by the types of the &optional, &rest or &key arguments.

How is set! defined in scheme?

How would you implement your own set! function in Scheme? A set! function is a destructive procedure that changes a value that is defined taking into account the previous value.
As noted in the comments, set! is a primitive in Scheme that must be provided by the implementation. Similarly, you can't implement the assignment operator, =, in most programming languages. In Common Lisp, setf can be extended (using setf-expanders) to allow (setf form value) to work on new kinds of forms.
Because Scheme's set! only modifies variable bindings (like Common Lisp's setq), it is still worth asking how can we implement functions like set-car! and other structural modifiers. This could be seen, in one sense, as a generalization of assignment to variables, but because lexical variables (along with closures) are sufficient to represent arbitrarily complex structures, it can also be seen as a more specialized case. In Scheme (aside from built in primitives like arrays), mutation of object fields is a specialization, because objects can be implemented by lexical closures, and implemented in terms of set!. This is a typical exercise given when showing how structures, e.g., cons cells, can be implemented using lexical closures alone. Here's an example that shows the implementation of single-value mutable cells::
(define (make-cell value)
(lambda (op)
(case op
((update)
(lambda (new-value)
(set! value new-value)))
((retrieve)
(lambda ()
value)))))
(define (set-value! cell new-value)
((cell 'update) new-value))
(define (get-value cell)
((cell 'retrieve)))
Given these definitions, we can create a cell, that starts with the value 4, update the value to 8 using our set-value!, and retrieve the new value:
(let ((c (make-cell 4)))
(set-value! c 8)
(get-value c))
=> 8
As has been mentioned, set! is a primitive and can't be implemented as a procedure. To really understand how it works under the hood, I suggest you take a look at the inner workings of a Lisp interpreter. Here's a great one to start: the metacircular evaluator in SICP, in particular the section titled "Assignments and definitions". Here's an excerpt of the parts relevant for the question:
(define (eval exp env)
(cond ...
((assignment? exp) (eval-assignment exp env))
...
(else (error "Unknown expression type -- EVAL" exp))))
(define (assignment? exp)
(tagged-list? exp 'set!))
(define (eval-assignment exp env)
(set-variable-value! (assignment-variable exp)
(eval (assignment-value exp) env)
env)
'ok)
(define (set-variable-value! var val env)
(define (env-loop env)
(define (scan vars vals)
(cond ((null? vars)
(env-loop (enclosing-environment env)))
((eq? var (car vars))
(set-car! vals val))
(else (scan (cdr vars) (cdr vals)))))
(if (eq? env the-empty-environment)
(error "Unbound variable -- SET!" var)
(let ((frame (first-frame env)))
(scan (frame-variables frame)
(frame-values frame)))))
(env-loop env))
In the end, a set! operation is just a mutation of a binding's value in the environment. Because a modification at this level is off-limits for a "normal" procedure, it has to be implemented as a special form.
Can't, can't, can't. Everyone is so negative! You can definitely do this in Racket. All you need to do is to define your own "lambda" macro, that introduces mutable cells in place of all arguments, and introduces identifier macros for all of those arguments, so that when they're used as regular varrefs they work correctly. And a set! macro that prevents the expansion of those identifier macros, so that they can be mutated.
Piece of cake!
set! modifies a binding between a symbol and a location (anyting really). Compilers and Interpreters would treat set! differently.
An interpreter would have an environment which is a mapping between symbols and values. Strictly set! changes the value of the first occurrence of the symbol to point to the result of the evaluation of the second operand. In many implementations you cannot set! something that is not already bound. In Scheme it is expected that the variable is already bound. Reading SICP or Lisp in small pieces and playing with examples would make you a master implementer of interpreters.
In a compilers situation you don't really need a symbol table. You can keep evaluated operands on a stack and set! need either change what the stack location pointed to or if it's a free variable in a closure you can use assignment conversion. E.g. it could be boxed to a box or a cons.
(define (gen-counter start delta)
(lambda ()
(let ((cur start))
(set! start (+ cur delta))
cur)))
Might be translated to:
(define (gen-counter start delta)
(let ((start (cons start '()))
(lambda ()
(let ((cur (car start)))
(set-car! start (+ cur delta))
cur)))))
You might want to read Control-Flow Analysis of Higher-Order Languages where this method is used together with lots of information on compiler technique.
If you are implementing a simple interpreter, then it is not all that hard. Your environment will map from identifiers to their values (or syntactic keywords to their transformers). The identifier->value mapping will need to account for possible value changes. As such:
(define (make-cell value)
`(CELL ,value))
(define cell-value cadr)
(define (cell-value-set! cell value)
(set-car! (cdr cell) value))
...
((set-stmt? e)
(let ((name (cadr e))
(value (interpret (caddr e) env)))
(let ((cell (env-lookup name)))
(assert (cell? cell))
(cell-value-set! cell value)
'return-value-for-set!)))
...
With two other changes being that when you bind an identifier to a value (like in a let or lambda application) you need to extend the environment with something like:
(env-extend name (cell value) env)
and also when retrieving a value you'll need to use cell-value.
Of course, only mutable identifiers need a cell, but for a simple interpreter allocating a cell for all identifier values is fine.
In scheme there are 2 "global" environments.
There is an environment where you store your highest defined symbols and there is another environment where are stored the primitive functions and the global variables that represent parameters of the system.
When you write something like (set! VAR VAL), the interpreter will look for the binding of VAR.
You cannot use set! on a variable that was not bound. The binding is done either by define or by lambda or by the system for the primitive operators.
Binding means allocating a location in some environment. So the binding function has the signature symbol -> address.
Coming back to set!, the interpreter will look in the environment where VAR is bound. First it looks in local environment (created by lambda), then it its local parent environment, and so on, then in global environment, then in system environment (in that order) until it finds an environment frame that contains a binding for VAR.

Categories