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
On 16 Nov 2010, at 11:05, Hans Hübner wrote:
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
On Tue, Nov 16, 2010 at 2:22 AM, Pascal Costanza pc@p-cos.net wrote:
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 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
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
On Tue, Nov 16, 2010 at 1:15 PM, Nikodemus Siivola < nikodemus@random-state.net> wrote:
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
On 17 November 2010 00:07, Scott L. Burson Scott@sympoiesis.com wrote:
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
On Tue, Nov 16, 2010 at 13:15, Nikodemus Siivola nikodemus@random-state.net wrote:
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.
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
On Tue, Nov 16, 2010 at 11:05 AM, Hans Hübner hans.huebner@gmail.com wrote:
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
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
Hans Hübner wrote:
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
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
On Dec 1, 2010, at 9:51 AM, Daniel Weinreb wrote:
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
On Thu, 2 Dec 2010, Ben Hyde wrote:
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
On Dec 2, 2010, at 4:38 PM, David Owen wrote:
On Thu, 2 Dec 2010, Ben Hyde wrote:
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
On Thu, 2 Dec 2010, Ben Hyde wrote:
On Dec 2, 2010, at 4:38 PM, David Owen wrote:
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
The system that Dan and I are working on does, in fact, have a 'with-accessors' macro that does just what you think.
On Dec 2, 2010, at 12:37 PM, Ben Hyde wrote:
On Dec 1, 2010, at 9:51 AM, Daniel Weinreb wrote:
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 pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro
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
Hans Hübner wrote:
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
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro