# Law of good style for CLOS

• To: commonloops.pa@Xerox.COM
• Subject: Law of good style for CLOS
• From: karl lieberherr <lieber@corwin.ccs.northeastern.edu>
• Date: Thu, 9 Jun 88 17:23:54 EDT
• Redistributed: commonloops.pa

```All the feedback we got on the Law of good style for CLOS has
prompted the Demeter team to find the proper generalization
of our Flavors/Smalltalk/C++ Law to the CLOS Law.
We give here our solution and explain it on some of the examples
which you sent us. A pretty complete explanation will be in our
OOPSLA '88 paper. A copy will be sent to Daniel Bobrow (as he
requested).
----- LAW OF DEMETER (CLOS, object version) -------------
All function calls inside a method M must have
one of the following method selection argument objects:
- M's argument objects or
- slot values of method selection argument classes of M.
(Objects created by the method and objects in global
variables are viewed as being passed by arguments.
A method selection argument is an argument which is used
for identifying the applicable methods.)
----------------------------------------------------------
This formulation is a natural generalization of the Flavors/
Smalltalk/C++ Law where there is exactly one method selection
argument (the first one). Note that there is no restriction on
non-generic function calls: they have 0 method selection arguments.
The Law requires the programmer to be aware whether a function
is generic (reversing our earlier point of view in our reply
to Scott Fahlmann's message).

Richard Gabriel invented the following example:

>(defmethod hack-positions  ((p1 position) (p2 position))
>  (let ((p1-x (x-coord p1))
>	(p1-y (y-coord p1))
>	(p2-x (x-coord p2))
>	(p2-y (y-coord p2)))
>    (let ((rho1 (sqrt (+ (* p1-x p1-x) (* p1-y p1-y))))
>	  (theta1 (atan p1-y p1-x))
>	  (rho2 (sqrt (+ (* p2-x p2-x) (* p2-y p2-y))))
>	  (theta2 (atan p2-y p2-x)))
>      (let* ((q (f rho2 theta2 rho1 theta1))
>	     (r (g q))
>	     (s (h r))
>	     (tt (i s))
>	     (u (j tt)))
>        (k u)))))

>Is the call (x-coord p1) a violation?  [It is not known whether it is a
>slot access or a function call.] Is the call (x-coord p2) a violation?
>[It is invoked on something besides the first argument to the method.]

NO VIOLATION since p1 and p2 are method selection arguments.

>Is the call to SQRT a violation?  [Its first argument is a number, but is
>not related to the first argument to the method.]

NO VIOLATION since + returns a new object.

How about the call to ATAN?
>[Its first argument is related to the second argument to the method
>via a LET variable.]

A VIOLATION if we assume that the objects returned by (x-coord p1)
and (y-coord p1) are not parts of p1. Otherwise no violation.

>How about the call to f?  [Its first argument is
>derived from something related to the second argument to the method.]

f is in GOOD STYLE since it gets all newly created objects.

>The call to g?  [Its first argument is indirectly related to the arguments to
>the method.

There is not enough type information here to tell.

>Karl, if you could answer whether anything in HACK-POSITIONS violates the law
>and why, we could begin to think about the issue you're raising. If nothing
>here violates the law, could *you* provide a method that violates the law?

Here is a simple example.
;;; A = B.
(defclass A () ((B :type B)))
;;; B = C.
(defclass B () ((C :type C)))
;;; C = D.
(defclass C () ((D :type D)))

(defmethod violation ((p1 A) (p2 Z))
(slot-value (slot-value (slot-value p1 'b) 'c)))

The idea is that it is a bad strategy to put a dependency on the
C class into this method which is "attached to" A and Z.
---------------------------------
Jim Kempf's remark promotes further encapsulation. It enhances the
Law but does not replace it.

>If a class is defined with accessor generic functions whose names are
>exported from the package, but are different from the slot names,
>which are not exported (or "hidden"), then the only access to the
>slots is through a functional interface. From the client's point of
>view, invoking a generic function on the object need not have any
>relation to slot access at all, much less an inherited or noninherited
>slot. This is an even stronger form of encapsulation, since the structure
>of the class is now completely hidden. Of course, an inheriting client

I fully agree. It is good that CLOS allows to generate such an interface.
------------------------------------
For completeness I include the Smalltalk-80 version:

----- LAW OF DEMETER (Smalltalk-80, object version) ----
In all message expressions inside a method M
the receiver must be one of the following objects:
- an argument object of M including objects in
pseudo variables self and super or
- an instance variable object of the class to which M
is attached.
(Objects created by the method and objects in global
variables are viewed as being passed by arguments.)
--------------------------------------------------------
I am very interested in your feedback regarding the understandability
of our formulation of the Law of good style.

-- Karl
```