Return-Path: Received: from CS.CMU.EDU by A.GP.CS.CMU.EDU id aa02993; 9 Sep 94 9:18:00 EDT Received: from aplcenmp.apl.jhu.edu by CS.CMU.EDU id aa00856; 9 Sep 94 9:17:31 EDT Received: by aplcenmp.apl.jhu.edu (4.1/SMI-4.1) id AA18318; Fri, 9 Sep 94 09:19:08 EDT Message-Id: <9409091319.AA18318@aplcenmp.apl.jhu.edu> From: Marty Hall Date: Fri, 9 Sep 1994 09:19:07 -0400 In-Reply-To: Mark Kantrowitz's message of Sep 8, 10:18pm X-Mailer: Mail User's Shell (7.2.3 5/22/91) To: mkant@CS.CMU.EDU Subject: Re: Common CLOS Blunders In "Re: Common CLOS Blunders" on Sep 8, you write: > Please send the latest version. Here's the ASCII. A PostScript version follows. I used bold in the body to indicate a builtin symbol instead of capitalizing it. - Marty (proclaim '(inline skates)) =========================================================================== Omitting parens in arglist in DEFMETHOD. ======================================== Writing (defmethod Area (Sq Square) ...) instead of (defmethod Area ((Sq Square)) ...) Lisp will accept the former, and think that you have two unspecialized arguments instead of one argument specialized as a Square. Missing parens around slot definition list in DEFCLASS. ======================================================= Writing (defclass Rectangle (Polygon) (Width ...) (Height ...)) instead of (defclass Rectangle (Polygon) ((Width ...) (Height ...))) Lisp will not accept the former, but the error message is not necessarily clear. Forgetting empty slot definition list if you don't define local slots in DEFCLASS. ===================================================================== Writing (defclass Square (Rectangle)) instead of (defclass Square (Rectangle) ()) Lisp will not accept the former. Referring to class name instead of instance variable in DEFMETHOD. ================================================================== Writing (defmethod Area ((Sq Square)) (* (Width Square) (Width Square))) instead of (defmethod Area ((Sq Square)) (* (Width Sq) (Width Sq))) Lisp may give a warning about an unknown free variable, but probably won't even do that if you type the defmethod directly into the Lisp Listener (Lucid doesn't). So you might not get an error until run-time. Forgetting accessors are functions and thus could conflict with built-in function names. =============================================================== E.g. writing (defclass Graphical-Object () ((Position :accessor Position))) Lisp will not accept this since you cannot redefine the built-in position function. Putting the new value last instead of first in the definition of a SETF method. ================================================================== Writing (defmethod (setf Area) ((Sq Square) (New-Area number)) (setf (Width Sq) (sqrt New-Area))) instead of (defmethod (setf Area) ((New-Area number) (Sq Square)) (setf (Width Sq) (sqrt New-Area))) Lisp will accept the former, and then users are puzzled as to why (setf (Area Square-1) 10) doesn't work. Putting the new value last instead of first in a call to a :writer method. ================================================================== E.g given (defclass Circle () ((Radius :reader Radius :writer Set-Radius :initform 5))) (setq Circle-1 (make-instance 'Circle)) Writing (Set-Radius Circle-1 10) Instead of (Set-Radius 10 Circle-1) Confusion about doc strings in DEFMETHOD. ========================================= People often do (defmethod Area ((Rect Rectangle)) "WIDTH times HEIGHT of the rectangle" (* (Width Rect) (Height Rect))) without clearly thinking about what that might mean. Some people think it will make a doc string on the generic function that (documentation 'Area 'function) or the equivalent emacs keystrokes will retrieve. Others vaguely expect it to make a doc string on each separate method, and that the emacs doc-string retrieving keystroke (which just calls documentation) will somehow automagically be able to figure out which method it applies to. In fact, Lisp will accept this, but put the documentation on the method *object*, which beginners probably know nothing about. Use the :documentation entry in defgeneric to put a doc string on the generic function. Invalid :initargs are accepted by MAKE-INSTANCE. ================================================ On SPARC machines, both Lucid Common Lisp 4.1.1 (both the production and development compilers) and Harlequin LispWorks 3.1.0 accept unknown :initarg's without complaint, even at the highest safety settings. (defclass Foo () ((Slot-1 :accessor Slot-1 :initarg :Slot-1 :initform 5))) ;;; typo: SLOT1 instead of SLOT-1 (setq Test (make-instance 'Foo :Slot1 10)) [No error message] (Slot-1 Test) ==> 5 This is a bug in the implementation; all implementations are supposed to flag this as an error. Forgetting the class has to exist before any method that specializes upon it. ==================================================================== Lisp programmers are used to being able to define functions in any order, where even if Foo calls Bar, Foo can be defined first. But (defmethod Area ((Rect Rectangle)) ...) (defclass Rectangle (Polygon) ...) is illegal. You have to define the class first. Changing a method to apply to a more general class does not supersede previous method. ===================================================================== E.g. a user writes (defmethod Half-Area ((Rect Filled-Rectangle)) (/ (Area Rect) 2)) Then they notice that this functionality could apply to all Rectangles, not just Filled-Rectangles. So they change the class, with their intuition being that they are replacing the old definition, when in fact they are adding a new, less-specific method. They then later add a call to float to avoid getting a ratio back. (defmethod Half-Area ((Rect Rectangle)) (float (/ (Area Rect) 2))) Then they are puzzled as to why their new definition appears not to have taken effect, since they are testing it on an instance of Filled-Rectangle, which still gets the old, more-specific definition.