Subject: Re: Design patterns for Lisp From: Erik Naggum <firstname.lastname@example.org> Date: Sun, 25 Nov 2001 23:49:12 GMT Newsgroups: comp.lang.lisp Message-ID: <email@example.com> * Software Scavenger | In the recent thread about design patterns, RPG's definition of patterns | seemed to be approximately that patterns are components of a programmer's | knowledge, which differentiate between more and less experienced | programmers. Such components seem to me to include algorithms and good | usage of a programming language, along with other kinds of knowledge. If I have understood this patterns thing correctly, these would be some of the patterns of design in Common Lisp: 1 Design algorithms to work with the highest meaningful class in the class hierarchy. E.g., design them for sequence rather than specifically for strings or lists, or vector rather than specifically for string. 2 Accept start and end arguments in sequence function to avoid unnecessary consing. Consequently, use them rather than inventing your own indexing and termination scheme. 3 When accepting an "end" position, it is exclusive. (The "start" position is likewise inclusive.) A nil end argument means the natural end of the sequence. Accept an explicit end argument, do not depend on defaulting. I.e., when the start and end arguments are equal, the sequence is empty. 4 If your algorithm scans a sequence in one direction one element at a time, accept a from-end argument. Consequently, one does not need to call it with reversed arguments. 5 Use iteration rather than recursion to scan a sequence one element at a time. (Reserve recursion for situations where you _require_ a stack.) I.e., Common Lisp is not a dialect of Scheme. 6 When iterating over something to collect elements into a list, use loop with collect, or push onto a list which you nreverse before returning with the following template, (do (... (list '())) (... (nreverse list)) ... (push ... list) ...) or stuff new items onto the end of a list using the following template (which is usually what loop uses) (do* (... (head (cons nil nil)) (tail head)) (... (cdr head)) ... (setf tail (setf (cdr tail) (cons ... nil))) ...)) 7 Design function interfaces so they can accept designators. I.e., study and use the designators already designed-into Common Lisp. When designing the functional interface for a new class hierarchy, that, say, accept an "employee" instance, and you find employees using a string or an integer, design and use an "employee designator" that turns strings and integers into employee instances whenever only an employee instance makes sense. Except for item 6, I do not think these are issues of abstraction or macrology. The patterns in 6 may be cast into macro form, but the degree of variation may simply be too large to make general macros useful, which is kind of what I expect from patterns: If they were general enough, they _would_ be abstractions. Now, RPG once told me that I sounded like I had not understood patterns, so, Dick, if you read this and think it sounds like I have now, I would appreciate a more positive hint this time. :) \\\ -- The past is not more important than the future, despite what your culture has taught you. Your future observations, conclusions, and beliefs are more important to you than those in your past ever will be. The world is changing so fast the balance between the past and the future has shifted.