Subject: Re: Design patterns for Lisp
From: Erik Naggum <erik@naggum.net>
Date: Sun, 25 Nov 2001 23:49:12 GMT
Newsgroups: comp.lang.lisp
Message-ID: <3215720949746615@naggum.net>

* 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.