[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Nesting and the Law

Jim Kempf took strong exception to our ideas in writing:
	After reading your IEEE Computer article, I must unfortunately
	take strong exception to your Law of Demeter for CLOS.
This is a clear statement and requires a clear answer. 
We took his message (along with feedback from our students)
as an opportunity to make our Law more clear:

----- LAW OF DEMETER (CLOS, object version, revised) -------------
For all methods M, all function calls inside M must use only the
following objects as method selection arguments:
- M's argument objects or
- slot values of method selection argument classes of M.
(Objects created by the method, or by functions which
it calls, 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.)
Jim Kempf's objections stem from a mis-understanding of the phrase
'objects created within the method are treated as arguments'.
Our idea is that a generic function does not neccessarily have to contain
an explicit 'make-instance' call (or its equivalent) to cause the
creation of a new object.  If the generic function has within it
a call to a function which returns A Newly Created object then this
too is considered a creation of an object within the generic function.
Consider user defined constructor functions in C++.

The example which Jim Kempf cites as being in bad style is indeed in good 
style, for two reasons.

	(defmethod sum-of-sqrt-of-difference ((x float) (y float))
	  (+ (sqrt (- x y)) y))

1. The type of x and y is given as float.  If the result of the '-' application
is an instance of type float then, according to the Law as stated in 
IEEE Computer, June 88, this object is an instance of an argument type (float) 
and so it is a valid argument to the 'sqrt' function.

Point 1 above is a technical one and dependent on the Law's statement in terms
of types.  Point 2 is MUCH more relevant and involves the ideas above.

2. The application of the '-' to the x and y causes the creation of a new
instance of the float class which is initialized to a value representing
'x - y'. So calling '-' is just the same as creating a new object  and
giving the instance variables of that object some initial values depending
on the arguments and some procedural processing.  Thus the argument to the
sqrt function is again valid.

It is important to note that we haven't "thrown out the baby with the
bathwater" with this interpretation of 'object creation'.  A 
generic function has the authority to make decisions and operate
in ways which are DEPENDENT on the 'valid' objects (and their types),
but IS NOT ALLOWED to be
dependent on the sub-parts of those objects.  The deep structures of
the objects are hidden from the generic function.  This is a real and
important restriction on the programmer.

The second example given by Jim Kempf
	(defmethod output-document 
	  ((switches string) (filename pathname) (printer string))

	  (lpr printer (troff switches filename)))

	(defmethod lpr ((printer string) (document stream))
	   <<code to send document to the printer>>

	(defmethod troff ((switches string) (filename pathname))
	  <<code to do troff, might be in Lisp or a call to Unix>>
is again in good style since the object returned from
'troff' is a new object created and initialized to the appropriate value.

The Law of Demeter has implications for the nesting of function
calls. To discuss those implications, we classify  functions as follows:

A accessor function returns an object which exists before the
function was called.

A constructor function returns an object which did not exist before
the function was called.

The Law allows the nesting of constructor functions, as well as
the nesting of non-generic functions.
However, the Law prohibits functional composition of generic accessor functions.

-- Karl