For instance [running Clisp under Mac OSX], after
(defpackage PKA (:use "COMMON-LISP"))
(in-package PKA)
(defstruct POINT (x 1 :type number) (y 1 :type number))
(export '(POINT-x POINT-y make-POINT POINT))
we execute
(in-package user)
(use-package ' PKA)
(defvar *p* (make-POINT :x 3 :y 4))
Now I /can/ use `with-slots' with no calls to the slots,
e.g
> (with-slots (x y) *p* ) => NIL
However this
> (with-slots (x y) *p* (setq x 87))
yields
*** - SETF: The class #1=#<STRUCTURE-CLASS POINT> has no slot
named X
Ironically, `POINT' really /doesn't/ have a slot named, say,
"z", yet
(with-slots (x y) *p* (setq z 'WorksOk)) => WORKSOK
and, of course, doesn't mutate `*p*'.
================
It does not work to specify the package in the body of `with-slots':
> (with-slots (x y) *p* (setq PKA::x 'NotGoingWhereIWant))
> *p* => #S(POINT :X 3 :Y 4)
So *p* isn't mutated; rather
> PKA::x => NOTGOINGWHEREIWANT
================
It /does/ work to specify the package in both the
slot-name-list /and/ the body,
> (with-slots (PKA::x y) *p* (setq PKA::x 'Bah))
> *p* => #S(POINT :X BAH :Y 4)
but having to specify the package everywhere in the body
defeats the purpose of `with-slots' for me. I could just
use function POINT-x everywhere, which I /did/ export.
================
Summing up, I /could/ have exported "x" from package PKA.
However, minorly I don't wish to export such a common
name. Majorly, I might also have a MATRIX package that
has
(defstruct CELL (x 0 :type (unsigned-byte 4))
(y 0 :type (unsigned-byte 4))
)
(export '(CELL-x CELL-y make-CELL))
for locating a position in a matrix. I'd like to be able
to use
(with-slots (x y) *p* ...)
and
(with-slots (x y) obj-of-type-CELL ...)
at the same time, but [AFAIK] can't simultaneously import "x"
from both PKA and MATRIX into package `user'.
-- Jonathan LF King; Mathematics dept, Univ. of Florida
For instance [running Clisp under Mac OSX], after
(defpackage PKA (:use "COMMON-LISP"))
(in-package PKA)
(defstruct POINT (x 1 :type number) (y 1 :type number))
(export '(POINT-x POINT-y make-POINT POINT))
we execute
(in-package user)
(use-package ' PKA)
(defvar *p* (make-POINT :x 3 :y 4))
Now I /can/ use `with-slots' with no calls to the slots,
e.g
> (with-slots (x y) *p* ) => NIL
However this
> (with-slots (x y) *p* (setq x 87))
yields
*** - SETF: The class #1=#<STRUCTURE-CLASS POINT> has no slot
named X
Ironically, `POINT' really doesn't have a slot named, say,
The syntax for with-slots [1] is:
with-slots (slot-entry*) instance-form declaration* form*
slot-entry::= slot-name | (variable-name slot-name)
The variable name doesn't have to be the same as the slot name. So you
can do, for instance, the following:
CL-USER 1 > (defpackage pka)
#<The PKA package, 0/16 internal, 0/16 external>
CL-USER 2 > (in-package pka)
#<The PKA package, 0/16 internal, 0/16 external>
PKA 3 > (defclass point () (x y))
#<STANDARD-CLASS POINT 200C512B>
PKA 4 > (in-package cl-user)
#<The COMMON-LISP-USER package, 24/64 internal, 0/4 external>
CL-USER 5 >
(let ((point (make-instance 'pka::point)))
;; pka::x is the slot name, but x is the variable
(with-slots ((x pka::x)) point
(setf x 'some-value))
(slot-value point 'pka::x))
SOME-VALUE
Hope this helps,
//JT
[1] http://www.lispworks.com/documentation/HyperSpe/Body/m_w_slts.htm
> How do I use `with-slots' when I don't wish to, or can't,
> export the slot names?
>
> For instance [running Clisp under Mac OSX], after
>
> (defpackage PKA (:use "COMMON-LISP"))
> (in-package PKA)
> (defstruct POINT (x 1 :type number) (y 1 :type number))
> (export '(POINT-x POINT-y make-POINT POINT))
>
> we execute
>
> (in-package user)
> (use-package ' PKA)
> (defvar *p* (make-POINT :x 3 :y 4))
>
> Now I /can/ use `with-slots' with no calls to the slots,
> e.g
>
> > (with-slots (x y) *p* ) => NIL
Or other nasal daemons: WITH-SLOTS doesn't work on structures, in
general.
Use WITH-ACCESSORS:
(let ((p (make-point)))
(with-accessors ((x point-x) (y point-y)) p
(setf x 42)
p))
--> #S(POINT :X 42 :Y 1)
> It /does/ work to specify the package in both the
> slot-name-list /and/ the body,
>
> > (with-slots (PKA::x y) *p* (setq PKA::x 'Bah))
>
> > *p* => #S(POINT :X BAH :Y 4)
>
> but having to specify the package everywhere in the body
> defeats the purpose of `with-slots' for me. I could just
> use function POINT-x everywhere, which I /did/ export.
If the slot name is not exported, it means that the package designer
clearly didn't want you to access the slots directly. Even with CLOS
objects, use with-accessors (ie. a method that might be specialized for
the object, ensuring the invariants of the object, instead of directly
peeking and poking into the slot):
(in-package :cl-user)
(unuse-package :pka)
(delete-package :pka)
(defpackage :PKA
(:use "COMMON-LISP")
(:export "POINT-X" "POINT-Y" "MAKE-POINT" "POINT"))
(in-package :PKA)
(defclass POINT ()
((x :initform 1 :initarg :x :type real :accessor point-x)
(y :initform 1 :initarg :y :type real :accessor point-y)))
(defun make-point (&key (x 1) (y 1)) (make-instance 'point :x x :y y))
(defmethod print-object ((p point) stream)
(print-unreadable-object (p stream :type t :identity t)
(prin1 (list :x (if (slot-boundp p 'x) (slot-value p 'x) "#<UNBOUND>")
:y (if (slot-boundp p 'y) (slot-value p 'y) "#<UNBOUND>"))
stream))
p)
(in-package :cl-user)
(use-package :PKA)
(let ((p (make-point)))
(with-accessors ((x point-x) (y point-y)) p
(setf x 42)
p))
--> #<POINT (:X 42 :Y 1) #x0003349DAF90>
> (defstruct CELL (x 0 :type (unsigned-byte 4))
> (y 0 :type (unsigned-byte 4))
> )
> (with-slots (x y) *p* ...)
Not with structures.
--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Yes it does; thank you. I should have seen that.
Jonathan LF King Math dept, Univ. of Florida
PS: BTWay, this link
> [1] http://www.lispworks.com/documentation/HyperSpe/Body/m_w_slts.htm
gave an "Object not found" for me.
By "doesn't work" do you mean that the spec doesn't guarantee
that `with-slots' even /applies/ to a struct? Can an ANSI
implementation signal-an-error upon
`(with-slots <list of slots> <a strut>)' ?
Page
http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/mac_defstruct.html
has "If :type is not supplied, [then] the structure is represented
as an object of type structure-object.
defstruct without a :type option defines a class with the
structure name as its name. The metaclass of structure
instances is structure-class.
I don't understand classes, but is this paragraph saying
that a struct is viewed as a special restricted case of a
class?
That is, if there is an operator acting on classes, and
what-it-does makes sense for structs, is the intention of the
spec that this operator must also apply to structs?
================================================================
"Pascal J. Bourguignon" <p...@informatimago.com> continued:
> Use WITH-ACCESSORS:
>
> (let ((p (make-point)))
> (with-accessors ((x point-x) (y point-y)) p
> (setf x 42)
> p))
> --> #S(POINT :X 42 :Y 1)
Ok. Is `with-accessors' guaranteed to work with STRUCTS?
Is there an official list of operators that work on structs?
Also, is there a STRUCT or CLASS circumstance where
`with-slots' is preferable to `with-accessors'?
...
>
> If the slot name is not exported, it means that the package designer
> clearly didn't want you to access the slots directly.
In this case I was both the "package designer" and the "user".
The [possibly misguided] package designer did indeed intend that the
user access the slots --but /only/ via `with-slots'.
...
> Even with CLOS objects, use with-accessors (ie. a method that might
> be specialized for the object, ensuring the invariants of the object,
> instead of directly peeking and poking into the slot):
If the object is a struct, is `with-accessors' as fast as
`with-slots'? I have an application where running speed is
moderately important to me.
Pascal, thank you for your detailed reply.
-- Jonathan LF King Math dept, Univ. of Florida
http://www.math.ufl.edu/~squash/teaching.html
> On Jun 25, 1:51 am, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:
>> ...
>> WITH-SLOTS doesn't work on structures, in general.
>>
>
> By "doesn't work" do you mean that the spec doesn't guarantee
> that `with-slots' even /applies/ to a struct? Can an ANSI
> implementation signal-an-error upon
>
> `(with-slots <list of slots> <a strut>)' ?
>
>
> Page
>
> http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/mac_defstruct.html
>
> has "If :type is not supplied, [then] the structure is represented
> as an object of type structure-object.
> defstruct without a :type option defines a class with the
> structure name as its name. The metaclass of structure
> instances is structure-class.
>
> I don't understand classes, but is this paragraph saying
> that a struct is viewed as a special restricted case of a
> class?
> That is, if there is an operator acting on classes, and
> what-it-does makes sense for structs, is the intention of the
> spec that this operator must also apply to structs?
clhs with-slots says:
The macro with-slots translates an appearance of the slot name as a
variable into a call to slot-value.
clhs slot-value says:
The specific behavior depends on object's metaclass. An error is
never signaled if object has metaclass standard-class. An error is
always signaled if object has metaclass built-in-class. The
consequences are unspecified if object has any other metaclass--an
error might or might not be signaled in this situation. Note in
particular that the behavior for conditions and structures is not
specified.
Therefore, a raw call to slot-value or with-slots on a structure is
non-conforming. (You could still write conforming code using them on
structures, if you handle errors or avoid applying them when they don't
work on structures).
> ================================================================
>
> "Pascal J. Bourguignon" <p...@informatimago.com> continued:
>
>> Use WITH-ACCESSORS:
>>
>> (let ((p (make-point)))
>> (with-accessors ((x point-x) (y point-y)) p
>> (setf x 42)
>> p))
>> --> #S(POINT :X 42 :Y 1)
>
> Ok. Is `with-accessors' guaranteed to work with STRUCTS?
> Is there an official list of operators that work on structs?
with-accessors works with any accessor.
Basically, an accessor is a symbol which has a function definition, and
which is usable in setf. (eg. (SETF CAR) may not exist, and RPLACA
doesn't follow the writer protocol, where the new value is the first
argument, but CAR is still an accessor).
While clhs with-accessors speaks of instances and slots, the definition
of accessor refers to reader and writer and those are:
reader n. 1. a function that reads[1] a variable or slot.
writer n. a function that writes[1] a variable or slot.
Notice that the car of a cons cell are slots too, just like array slots,
and pathname components (which are accessed by accessors), etc.
Therefore reader and writer, and accessors really designate any function
reading or writing a value somewhere.
For example, I implement an accessor which keeps data stored in a file
in com.informatimago.common-lisp.cesarum.file:
(import 'com.informatimago.common-lisp.cesarum.file:sexp-file-contents)
(setf (sexp-file-contents "/tmp/a.data" :if-does-not-exist :create) 20)
(incf (sexp-file-contents "/tmp/a.data") 11)
(incf (sexp-file-contents "/tmp/a.data") 11)
--> 42
So one could write also:
(with-accessors ((contents sexp-file-contents)) "/tmp/a.data"
(incf contents))
--> 43
Well, (class-of "/tmp/a.data") --> #<BUILT-IN-CLASS STRING>
so we have always an instance.
> also, is there a struct or class circumstance where
> `with-slots' is preferable to `with-accessors'?
Accessors are functions, that may implement costly algorithms. For
example, if you want to implement a circle as a subclass of ellipse, you
could override the accessors for the small axis and the big axis:
(defmethod (setf small-axis) (new-value (self circle))
(setf (slot-value circle 'small-axis) new-value
(slot-value circle 'big-axis) new-value))
(defmethod (setf big-axis) (new-value (self circle))
(setf (small-axis self) new-value))
the first writer could be defined also as:
(defmethod (setf small-axis) (new-value (self circle))
(with-slots (small-axis big-axis) self
(setf small-axis new-value
big-axis new-value)))
SLOT-VALUE is a low level memory access (well, still moderated by the
metaclass, but it should be quite efficient, CL compiler being able for
standard-class to basically open code it). The same is not possible for
accessors which have to go thru the dispatching (and can be overloaded
with before, after or around methods).
So, WITH-SLOTS might be preferable when implementing a method that can
ensure the class invariant itself, and that needs efficient direct
access to the slot.
In general, WITH-ACCESSORS is preferable.
>> if the slot name is not exported, it means that the package designer
>> clearly didn't want you to access the slots directly.
>
> in this case i was both the "package designer" and the "user".
> the [possibly misguided] package designer did indeed intend that the
> user access the slots --but /only/ via `with-slots'.
If you want that, then don't define accessors, and export the slot
names:
(defpackage :PKA
(:use "COMMON-LISP")
(:export "X" "Y" "MAKE-POINT" "POINT"))
(in-package :PKA)
(defclass POINT ()
((x :initform 1 :initarg :x :type real)
(y :initform 1 :initarg :y :type real)))
(in-package :cl-user)
(use-package :PKA)
;; Here, the only way to access x and y in a point are using
;; with-slots or slot-value.
Unfortunately, this is not a good design, because that prevents you to
write things like:
(defmethod :before (setf point-x) (new-value (p point))
(erase-point p)
(call-next-method)
(draw-point p))
so that your display is updated when you move a point.
Or anything else similar.
>> even with clos objects, use with-accessors (ie. a method that might
>> be specialized for the object, ensuring the invariants of the object,
>> instead of directly peeking and poking into the slot):
>
> if the object is a struct, is `with-accessors' as fast as
> `with-slots'?
When you're dead, what difference does it make if your coffin is lined
with silk or cotton?
with-slots don't work on structure, therefore this question is
irrelevant.
That said, accessors to structure slots defined by defstruct tend to be
quite efficient (at least as efficient as slot-value).
> i have an application where running speed is
> moderately important to me.
Depends on the implementation. On some implementations, CLOS is more
optimized, and slot-value is faster on standard-objects than accessors
on structure-objects. On most implementations however, accessors on
structure-objects are faster than slot-value on standard-objects.
What you can do, is to define your own macros: DEFINE-ENTITY and
WITH-ENTITY-ATTRIBUTES, and have them expand to either a DEFSTRUCT or a
DEFCLASS after testing what's faster on the current implementation.
--
__pascal bourguignon__ http://www.informatimago.com/
a bad day in () is better than a good day in {}.
> gentsquash <gents...@gmail.com> writes:
>
>> How do I use `with-slots' when I don't wish to, or can't,
>> export the slot names?
>>
[snip attempt to use with-slots on struct]
>
> Or other nasal daemons: WITH-SLOTS doesn't work on structures, in
> general.
>
> Use WITH-ACCESSORS:
>
> (let ((p (make-point)))
> (with-accessors ((x point-x) (y point-y)) p
> (setf x 42)
> p))
> --> #S(POINT :X 42 :Y 1)
I have a tangential question. If one expected to access something,
somewhere, but didn't know what or where to find it how would you make a
function that would try to figure it out?
Alternatively, how would one ask an object (or structure) what slots it
has? How would you ask an accessor what sort of things it accesses?
--
William Clifford
> "Pascal J. Bourguignon" <p...@informatimago.com> writes:
>
>> gentsquash <gents...@gmail.com> writes:
>>
>>> How do I use `with-slots' when I don't wish to, or can't,
>>> export the slot names?
>>>
> [snip attempt to use with-slots on struct]
>>
>> Or other nasal daemons: WITH-SLOTS doesn't work on structures, in
>> general.
>>
>> Use WITH-ACCESSORS:
>>
>> (let ((p (make-point)))
>> (with-accessors ((x point-x) (y point-y)) p
>> (setf x 42)
>> p))
>> --> #S(POINT :X 42 :Y 1)
>
> I have a tangential question. If one expected to access something,
> somewhere, but didn't know what or where to find it how would you make a
> function that would try to figure it out?
If you don't know what to find, you need to know where to find it.
If you don't know where to find it, you need to know what to find.
If you know what to find, you could try to walk all the objects in the
image. But conformingly, there's now way to collect the root set: some
objects may be hidden in closures and lexical scopes. If you have a way
to find the contents of the closures and lexical scopes, or if the
implementation provides the root set or the live objects, then you can
search what you want from the root set, or from each of the live
objects, by data-walking them (just like the mark phase of a garbage
collector).
If you know where to find it, then you already know where to find it.
> Alternatively, how would one ask an object (or structure) what slots it
> has?
There's nothing in COMMON-LISP to do that.
For instances of subclasses of STANDARD-OBJECT, you could use the MOP
function CLOS:CLASS-SLOTS if it is available (in most implementations,
it is available, see also the CLOSER-MOP which provides a portability
layer to the different MOPs).
Here is an example of how I use it to implement ENSURE-CLASS-SLOT which
adds a slot to a class:
(defun convert-to-direct-slot-definition (class canonicalized-slot)
(apply (function make-instance)
(apply (function clos:direct-slot-definition-class) class canonicalized-slot)
canonicalized-slot))
(defun canonicalize-slot-definition (slotdef)
(list :name (CLOS:SLOT-DEFINITION-NAME slotdef)
:readers (CLOS:SLOT-DEFINITION-READERS slotdef)
:writers (CLOS:SLOT-DEFINITION-WRITERS slotdef)
:type (CLOS:SLOT-DEFINITION-TYPE slotdef)
:allocation (CLOS:SLOT-DEFINITION-ALLOCATION slotdef)
:initargs (CLOS:SLOT-DEFINITION-INITARGS slotdef)
:initform (CLOS:SLOT-DEFINITION-INITFORM slotdef)
:initfunction (CLOS:SLOT-DEFINITION-INITFUNCTION slotdef)))
(defun ensure-class-slot (class-name slot-name)
(let ((class (find-class class-name)))
(when class
;; finalize it before calling CLOS:CLASS-SLOTS
(make-instance class-name)
(unless (find slot-name (clos:class-slots class)
:key (function clos:slot-definition-name))
(clos:ensure-class
class-name
:direct-slots
(append (mapcar (function canonicalize-slot-definition) (CLOS:CLASS-DIRECT-SLOTS class))
(list (list :name slot-name
:initform 'nil
:initfunction (constantly nil)
:initargs (list (intern (string slot-name) "KEYWORD"))
:readers (list slot-name)
:writers (list `(setf ,slot-name))
:documentation "Generated by define-association"))))))
class))
For instances of subclasses of STRUCTURE-OBJECT, sometimes for instances
of CONDITION, PATHNAME, and for objects of other built-in types, it's
not possible. At least not portably (the standard doesn't prevent
implementations to let the user add slots to characters ot integers...).
> How would you ask an accessor what sort of things it accesses?
Again, conformingly, there's no way.
Well, you could try to enumerate all the symbols, find if they designate
a class or a type, try to find constructor functions (for classes, you
can use MAKE-INSTANCE), try to build an object of each class or type
(for classes, you could use the class prototype), and call each function
you've found on each type object, and see if that's a reader (or if it
produces an error, but a reader may produce an error if the object has
an unbound slot, and class prototypes have all slots unbound), and try
to call the function with a new-value argument and see if that changes
the values returned by the reader (of course, you would have to check
that readers return at least twice the same value before that). Some
reader and writer may actually need more than two arguments, but they
wouldn't be normal plain readers and writers of normal plain slots.
So, with enough error handling, you should be able to break your image
(what did you expect, by calling random functions on random objects!
Some of them must change the state of the image in some irreparable
way!) and collect a list of potential tuples (type reader writer).
Finally, if you want to do introspection on your data, you can define it
with your own macros doing the needed book-keeping, implementing your
own introspection layer. See for example for structures:
http://groups.google.com/group/comp.lang.lisp/msg/34681fc951fb42b2
http://groups.google.com/group/comp.lang.lisp/msg/8e06a7470724aa38
http://groups.google.com/group/comp.lang.lisp/msg/f687db3424753775