Discussion:
When to use SLOT-VALUE...
Hans Hübner
2010-11-16 10:05:25 UTC
Permalink
Hi,

The company I work for has a Common Lisp style guide that generally
disallows using SLOT-VALUE. Instead, accessor should be used so that:
BEFORE and :AFTER methods are always invoked when accessing a slot.
Generally, I think this is a good idea when looking at classes and instances
from the outside. Slots should be considered as being an implementation
detail, and users (i.e. client code and derived class methods) should not
make assumptions about how functionality is implemented.

Now, I often have the need for class instances that are constant in some
respect, i.e. some properties of the instance that are implemented using
slots can't directly be changed. I often declare such slots havin only a:
READER in the class definition, which makes the read-only nature of this
slot apparent right away.

Of course, such slots need to be initialized somehow. An :INITARG sometimes
does the trick, but it is more common that the value of such slots is
calculated and recalculated during the lifetime of the instance, and as such
the slot's value must be set.

Now, from the perspective of seeing the class declaration as documenting the
visible behavior of instances of a class, it does not seem to be proper to
declare an accessor to be used in class-internal code so that the slot's
value can be updated. Instead, I think that it is better to use SLOT-VALUE
to mess with the guts of an instance from code that is part of the guts
itself.

Of course, one may want to argue that DEFCLASS forms should not be
considered to be an interface definition. Instead, one could call for a
series of DEFGENERIC forms to define the external interface of some "module"
and make class definitions be internal. From a more practical perspective,
though, class definitions in CL serve both as interface and implementation
definition, thus it seems to be appropriate using the mechanisms provided by
CLOS to support both uses.

How do others use or avoid SLOT-VALUE? Is it frowned upon in your company's
or project's (verbal) style guide?

Thanks for your input,
Hans
Pascal Costanza
2010-11-16 10:22:36 UTC
Permalink
Hi,
The company I work for has a Common Lisp style guide that generally disallows using SLOT-VALUE. Instead, accessor should be used so that: BEFORE and :AFTER methods are always invoked when accessing a slot. Generally, I think this is a good idea when looking at classes and instances from the outside. Slots should be considered as being an implementation detail, and users (i.e. client code and derived class methods) should not make assumptions about how functionality is implemented.
Now, I often have the need for class instances that are constant in some respect, i.e. some properties of the instance that are implemented using slots can't directly be changed. I often declare such slots havin only a: READER in the class definition, which makes the read-only nature of this slot apparent right away.
Of course, such slots need to be initialized somehow. An :INITARG sometimes does the trick, but it is more common that the value of such slots is calculated and recalculated during the lifetime of the instance, and as such the slot's value must be set.
Now, from the perspective of seeing the class declaration as documenting the visible behavior of instances of a class, it does not seem to be proper to declare an accessor to be used in class-internal code so that the slot's value can be updated. Instead, I think that it is better to use SLOT-VALUE to mess with the guts of an instance from code that is part of the guts itself.
Of course, one may want to argue that DEFCLASS forms should not be considered to be an interface definition. Instead, one could call for a series of DEFGENERIC forms to define the external interface of some "module" and make class definitions be internal. From a more practical perspective, though, class definitions in CL serve both as interface and implementation definition, thus it seems to be appropriate using the mechanisms provided by CLOS to support both uses.
Note that it is always possible to have several accessors with different names. So you could define something like this:

(defclass foo ()
((some-slot :reader official-slot-reader :accessor %internal-slot-accessor) ...))

I recall doing this in some code. I agree that using slot-value directly is stylistically the better option, but it is not unusual that the automatically generated readers/writers are more efficient than the equivalent slot-value counterparts.
How do others use or avoid SLOT-VALUE? Is it frowned upon in your company's or project's (verbal) style guide?
In general, I agree with you that slot-value has its uses for internal implementation details. A favorite example of mine is delayed initialization of slots:

(defclass bar ()
((some-slot :reader some-slot-reader)))

(defmethod slot-unbound ((class t) (instance bar) (slot (eql 'some-slot)))
(setf (slot-value instance 'some-slot)
... initialization code ...))

I think using a writer for such a case is conceptually wrong. (An internal writer, as above, is ok for efficiency reasons, although the performance difference shouldn't matter for such delayed initialization anyway.)


Best,
Pascal
--
Pascal Costanza, mailto:pc-***@public.gmane.org, http://p-cos.net
Vrije Universiteit Brussel
Software Languages Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Scott L. Burson
2010-11-16 20:58:20 UTC
Permalink
Post by Pascal Costanza
Note that it is always possible to have several accessors with different
(defclass foo ()
((some-slot :reader official-slot-reader :accessor
%internal-slot-accessor) ...))
I like this, actually, because it warns the reader that although the slot is
not to be written by clients, it may nonetheless change; the client mustn't
assume it's constant. Personally I think this is better than using
SLOT-VALUE, unless the only time it's written is for initialization.

-- Scott
Nikodemus Siivola
2010-11-16 21:15:20 UTC
Permalink
One angle that hasn't been mentioned yet is efficiency.

There's a fairly well known optimization (permutation vectors) that allows

(defmethod foo ((x bar))
...
(slot-value x 'quux)
...)

to be very efficient as long as X isn't assigned to (and as long as
there is not SLOT-VALUE-USING-CLASS, etc) -- not as fast as plain
structure accessors, but basically at the cost of two memory
indirections. A laugh compared to calling a generic accessor.

That said, I *think* permutation vectors can be extended to accessors
as well, but I don't know how commonly that is implemented (eg. SBCL
at least currently doesn't) -- whereas most(?) implementations,
particularly those deriving their CLOS from PCL probably implement the
basic permutation vector optimization.

Of course, readability and maintainability trump efficiency any time,
as long as the code if efficient enough for its intended purpose.

Still, something to be aware of when choosing between accessors and SLOT-VALUE.

Cheers,

-- Nikodemus
Scott L. Burson
2010-11-16 22:07:02 UTC
Permalink
On Tue, Nov 16, 2010 at 1:15 PM, Nikodemus Siivola <
Post by Nikodemus Siivola
I *think* permutation vectors can be extended to accessors
as well, but I don't know how commonly that is implemented (eg. SBCL
at least currently doesn't)
Presumably the automatically generated methods for the accessor use the
optimization internally, but the compiler can't inline those methods -- it
has to go through the generic function call, as you might have overridden
the accessor with an explicit method on a subclass, or added a daemon
method, or something.

-- Scott
Nikodemus Siivola
2010-11-16 22:18:19 UTC
Permalink
Post by Scott L. Burson
Presumably the automatically generated methods for the accessor use the
optimization internally, but the compiler can't inline those methods -- it
Basically yes. (Also, in addition to Andreas' caveat about
implementation differences, note that the optimization I mentioned can
only be utilized in DEFMETHOD bodies, not inside a DEFUN -- or at
least it drifts into the realm of heroic optimizations there.)

Cheers,

-- Nikodemus
Andreas Fuchs
2010-11-16 22:11:03 UTC
Permalink
On Tue, Nov 16, 2010 at 13:15, Nikodemus Siivola
Post by Nikodemus Siivola
Of course, readability and maintainability trump efficiency any time,
as long as the code if efficient enough for its intended purpose.
I'm not sure that it's entirely right to say slot-value will
definitely be faster. It may be faster in the cases you mention on
SBCL, but in one of our Allegro CL-based products, we just saw a 5-6%
improvement in performance on very common operations when we switched
from using slot-value to accessors (I think, almost exclusively).

That the change that made stuff faster also made it more readable
makes me very happy.
--
Andreas Fuchs, (http://|im:asf@|mailto:asf@)boinkor.net, antifuchs
Hans Hübner
2010-11-22 12:51:25 UTC
Permalink
By private email, James Anderson pointed out that accessors are
traceable, whereas SLOT-VALUE is not. To me, that is the most
convincing argument for always using accessors, even in class-internal
initialization code.

Thanks for all your input!

-Hans
Post by Hans Hübner
Hi,
The company I work for has a Common Lisp style guide that generally
BEFORE and :AFTER methods are always invoked when accessing a slot.
Generally, I think this is a good idea when looking at classes and instances
from the outside.  Slots should be considered as being an implementation
detail, and users (i.e. client code and derived class methods) should not
make assumptions about how functionality is implemented.
Now, I often have the need for class instances that are constant in some
respect, i.e. some properties of the instance that are implemented using
READER in the class definition, which makes the read-only nature of this
slot apparent right away.
Of course, such slots need to be initialized somehow. An :INITARG sometimes
does the trick, but it is more common that the value of such slots is
calculated and recalculated during the lifetime of the instance, and as such
the slot's value must be set.
Now, from the perspective of seeing the class declaration as documenting the
visible behavior of instances of a class, it does not seem to be proper to
declare an accessor to be used in class-internal code so that the slot's
value can be updated.  Instead, I think that it is better to use SLOT-VALUE
to mess with the guts of an instance from code that is part of the guts
itself.
Of course, one may want to argue that DEFCLASS forms should not be
considered to be an interface definition. Instead, one could call for a
series of DEFGENERIC forms to define the external interface of some "module"
and make class definitions be internal. From a more practical perspective,
though, class definitions in CL serve both as interface and implementation
definition, thus it seems to be appropriate using the mechanisms provided by
CLOS to support both uses.
How do others use or avoid SLOT-VALUE? Is it frowned upon in your company's
or project's (verbal) style guide?
Thanks for your input,
Hans
Daniel Weinreb
2010-12-01 14:51:33 UTC
Permalink
Sorry for the delay; here are my comments.

Meta-point: I prefer to work out these issues by first disregarding
speed issues, and figuring out what the best semantics is. Then,
later, if there is real need for speedup, we can do that, but keep in
place the original intention of the code, for the benefit of future
programmers working with the code. So, separate semantics from speed.

Hans raised what I think are two separate issues, both of them very
interesting, which I'd like to address separately, and I'll do it
in two emails since there may be two separate threads.

Using slot-value inside initialization methods.

Background: Some objects have a set of invariants. To be more
precise, the state of the object as seen by its callers is represented
internally by more than one thing, and the state of those things must
be kept consonant with each other in order for the state seen by the
callers to make sense at the caller's level of abstraction. Ideally,
these invariants should be carefully commented; in practice
programmers often leave them out, which is too bad.

A "consistent state" means that the invariants are true. When there
is no method actively running that's "inside the object", i.e. at the
level of abstraction below the "consistent view", namely the ones that
*makes* things consistent, then the object must be consistent.

The methods called by the callers (1) expect to find the object in a
consistent state, and (2) must leave the object in a consistent state
when they terminate, whether they terminate normally (return) or
abruptly (signal, return, throw, etc.)

It is possible that there are :before or :after methods on the reader
or writer. In fact, it's even possible that the primary method was
overridden, if this is a subclass. These methods must also keep the
object in a consistent state.

Under normal circumstances, to use slot-value operates at a lower
level of abstraction. This is, in general, dangerous, beacause
setting a slot-value could subvert the higher level's goal of keeping
the object consistent. (If you are old enough to remember this, it's
like "peek and poke", i.e. reading and writing words in memory that
are really implementing some higher-level construct.)

However, initialize-instance is not a normal circumstance.

As Hans says, for a CLOS object, sometimes :initform is too simple,
and you need to have an initialize-instance method. While the
instance is being initialized, in general it is not yet in its
consistent state. The invariant says that when the object has been
created such that it's ready to be seen by callers, it must be
consistent, but not until then. Therefore, you do NOT want to invoke
methods during the initialize-instance that will assume that the
object is consistent. That inclues :before and :after methods (and
overridden primaries) of the accessors.

So I think Hans is right. Not only is it OK to use slot-value here,
in fact it should be mandatory, or at least recommended, or
encouraged, or considered as the first option, or something.
(There might be times when you really know that the
method is right, although that might actually be a modularity
violation; too complicated to go into here.)

-- Dan
Post by Hans Hübner
Hi,
The company I work for has a Common Lisp style guide that generally
BEFORE and :AFTER methods are always invoked when accessing a slot.
Generally, I think this is a good idea when looking at classes and
instances from the outside. Slots should be considered as being an
implementation detail, and users (i.e. client code and derived class
methods) should not make assumptions about how functionality is
implemented.
Now, I often have the need for class instances that are constant in
some respect, i.e. some properties of the instance that are
implemented using slots can't directly be changed. I often declare
such slots havin only a: READER in the class definition, which makes
the read-only nature of this slot apparent right away.
Of course, such slots need to be initialized somehow. An :INITARG
sometimes does the trick, but it is more common that the value of such
slots is calculated and recalculated during the lifetime of the
instance, and as such the slot's value must be set.
Now, from the perspective of seeing the class declaration as
documenting the visible behavior of instances of a class, it does not
seem to be proper to declare an accessor to be used in class-internal
code so that the slot's value can be updated. Instead, I think that
it is better to use SLOT-VALUE to mess with the guts of an instance
from code that is part of the guts itself.
Of course, one may want to argue that DEFCLASS forms should not be
considered to be an interface definition. Instead, one could call for
a series of DEFGENERIC forms to define the external interface of some
"module" and make class definitions be internal. From a more practical
perspective, though, class definitions in CL serve both as interface
and implementation definition, thus it seems to be appropriate using
the mechanisms provided by CLOS to support both uses.
How do others use or avoid SLOT-VALUE? Is it frowned upon in your
company's or project's (verbal) style guide?
Thanks for your input,
Hans
------------------------------------------------------------------------
_______________________________________________
pro mailing list
http://common-lisp.net/cgi-bin/mailman/listinfo/pro
Ben Hyde
2010-12-02 17:37:24 UTC
Permalink
Post by Daniel Weinreb
The methods called by the callers (1) expect to find the object in a
consistent state, and (2) must leave the object in a consistent state
when they terminate, whether they terminate normally (return) or
abruptly (signal, return, throw, etc.)
It is possible that there are :before or :after methods on the reader
or writer. In fact, it's even possible that the primary method was
overridden, if this is a subclass. These methods must also keep the
object in a consistent state.
I find it curious, my reaction to that. In part it is warm and
nostalgic, as there was a time when I was more enthusiastic about
designing in that mind set. I am less so now. I now tend to open
module boundaries. I'm much less shy about revealing
implementations. I find it more pliable. And I tighten things down
in that kind of righteous way only if the code manages to survive long
enough to get the honor of maintenance and a large cliental.

Anyhow. Recall that with-slots expands to slot-value. That leads me
to wonder. Given that with-slots and slot-value are couple, why
haven't I observed analogous couple (with-fields and field-value say)
for accessors.

- ben
Scott McKay
2010-12-02 21:50:12 UTC
Permalink
The system that Dan and I are working on does, in fact,
have a 'with-accessors' macro that does just what you think.
Post by Ben Hyde
Post by Daniel Weinreb
The methods called by the callers (1) expect to find the object in a
consistent state, and (2) must leave the object in a consistent state
when they terminate, whether they terminate normally (return) or
abruptly (signal, return, throw, etc.)
It is possible that there are :before or :after methods on the reader
or writer. In fact, it's even possible that the primary method was
overridden, if this is a subclass. These methods must also keep the
object in a consistent state.
I find it curious, my reaction to that. In part it is warm and
nostalgic, as there was a time when I was more enthusiastic about
designing in that mind set. I am less so now. I now tend to open
module boundaries. I'm much less shy about revealing
implementations. I find it more pliable. And I tighten things down
in that kind of righteous way only if the code manages to survive long
enough to get the honor of maintenance and a large cliental.
Anyhow. Recall that with-slots expands to slot-value. That leads me
to wonder. Given that with-slots and slot-value are couple, why
haven't I observed analogous couple (with-fields and field-value say)
for accessors.
- ben
_______________________________________________
pro mailing list
http://common-lisp.net/cgi-bin/mailman/listinfo/pro
David Owen
2010-12-02 21:38:04 UTC
Permalink
Post by Ben Hyde
Anyhow. Recall that with-slots expands to slot-value. That leads
me to wonder. Given that with-slots and slot-value are couple, why
haven't I observed analogous couple (with-fields and field-value
say) for accessors.
Perhaps you are looking for WITH-ACCESSORS?

-David
Ben Hyde
2010-12-02 22:02:26 UTC
Permalink
Post by David Owen
Post by Ben Hyde
Anyhow. Recall that with-slots expands to slot-value. That leads
me to wonder. Given that with-slots and slot-value are couple, why
haven't I observed analogous couple (with-fields and field-value
say) for accessors.
Perhaps you are looking for WITH-ACCESSORS?
Indeed, I'm delighted to discover there is something in the language
I've not used. thanks!

Further I didn't know that setq turns into setf with the help of
symbol macro.

What a fun embarrassment this is! :)

thanks
David Owen
2010-12-02 22:24:04 UTC
Permalink
Post by Ben Hyde
Post by David Owen
Perhaps you are looking for WITH-ACCESSORS?
Indeed, I'm delighted to discover there is something in the language
I've not used. thanks!
Further I didn't know that setq turns into setf with the help of symbol
macro.
Heh, I had the hyperspec open and also didn't realize that until you
pointed it out!

-David

Daniel Weinreb
2010-12-01 14:53:40 UTC
Permalink
You considered the idea that when you write a library (internal
module, whatever) that defines a new type of Lisp object, it should
not necessarily be apparent to the caller whether the implementation
of objects of that type happen to use CLOS.

Hans: Of course, one may want to argue that DEFCLASS forms should not
be considered to be an interface definition. Instead, one could call
for a series of DEFGENERIC forms to define the external interface of
some "module" and make class definitions be internal.

In addition to this, you'd use a factory function, rather than having
the client call make-instance, to hide the CLOS nature of the type.

I agree with you about this, except that I would go farther and use
packages.

In fact, I wrote a library in this style, called fhash. It creates an
abstraction that implements a map, just like Common Lisp hash-tables,
except that internally it uses linear search when the number of items
is small, and upgrades to to a real hash table when the number of
items gets large.

This is very much like what Hans is talking about, with one slight
difference. In the fhash library, I took the position that Common
Lisp's mechanism for encapsulation is NOT CLOS, not object-oriented
programing, but packages.

The packge declaration says:

(:export
#:fhash-table
#:make-fhash-table
#:getfhash
#:putfhash
#:remfhash
#:clrfhash
#:mapfhash
#:fhash-table-p
#:fhash-table-count
#:fhash-table-size
#:fhash-table-test
#:with-fhash-table-iterator)

More about why using packages is right, in subsequent
mail, since that could turn into a separate thread.

-- Dan
Post by Hans Hübner
Hi,
The company I work for has a Common Lisp style guide that generally
BEFORE and :AFTER methods are always invoked when accessing a slot.
Generally, I think this is a good idea when looking at classes and
instances from the outside. Slots should be considered as being an
implementation detail, and users (i.e. client code and derived class
methods) should not make assumptions about how functionality is
implemented.
Now, I often have the need for class instances that are constant in
some respect, i.e. some properties of the instance that are
implemented using slots can't directly be changed. I often declare
such slots havin only a: READER in the class definition, which makes
the read-only nature of this slot apparent right away.
Of course, such slots need to be initialized somehow. An :INITARG
sometimes does the trick, but it is more common that the value of such
slots is calculated and recalculated during the lifetime of the
instance, and as such the slot's value must be set.
Now, from the perspective of seeing the class declaration as
documenting the visible behavior of instances of a class, it does not
seem to be proper to declare an accessor to be used in class-internal
code so that the slot's value can be updated. Instead, I think that
it is better to use SLOT-VALUE to mess with the guts of an instance
from code that is part of the guts itself.
Of course, one may want to argue that DEFCLASS forms should not be
considered to be an interface definition. Instead, one could call for
a series of DEFGENERIC forms to define the external interface of some
"module" and make class definitions be internal. From a more practical
perspective, though, class definitions in CL serve both as interface
and implementation definition, thus it seems to be appropriate using
the mechanisms provided by CLOS to support both uses.
How do others use or avoid SLOT-VALUE? Is it frowned upon in your
company's or project's (verbal) style guide?
Thanks for your input,
Hans
------------------------------------------------------------------------
_______________________________________________
pro mailing list
http://common-lisp.net/cgi-bin/mailman/listinfo/pro
Loading...