An Ode To LOOP
A Hidden Lesson in Lisp Style
LOOP: the venerable iteration construct in Common Lisp. Much maligned, much loved, and much to teach. A short post.
Thou still unresting ox of programming Thou working dog of Lispers who control Pithy key phrases that make expressing Iterations diverse so much less dull What brain-mad coder taunts about thy faults of extension or learning, or of both, on Reddit or the sloughs of Planet Lisp? What noobs or frauds are these? Why do they loathe? What argument might prompt their swifter halt? Can I convince with 'passioned words so crisp?
With apologies to the ghost of Keats
A LOOP EXPRESSION
Assume there exists a hash table called
person-table that stores instances of some
person class, keyed by first names.
Now, consider the following
LOOP expression, and keep this example in mind as you peruse the rest of this post.
(loop for name being the hash-key of person-table for person being the hash-value of person-table for seen from 0 when (string-equal name "larry") collect person into named-larry and do (format t "Hi, I'm ~a~%" (full-name person)) when (string-equal name "darryl") count name into darryl-count while (< darryl-count 1000) finally (return (values named-larry darryl-count seen)))
LOOP has some defects, some boons, and some lessons to teach.
LOOP sucks because of ...
- Extensibility: it cannot be used to iterate over exotic data containers. Just ranges, lists (in two ways), vectors, and hash tables.
- Lispiness: it doesn't look like the rest of Common Lisp. Note the relative lack of parentheses in the above!
- Superfluity: it is an entire extra language unto itself, with hard-to-recall keywords and syntax. E.g.
being the hash-key of ....
LOOP is great because of ...
- Versatility: Even though it isn't extensible, it is still pretty darn versatile. With
minimize; you run code
untilsome condition is true; you can check that a condition is
nevertrue, or that
thereisat least one true instance of the condition; you can define local variables, pattern match on binding forms, and iterate over most built-in data structures; and on and on...
- Readability: Despite feeling odd,
LOOPexpressions are fairly descriptive of what they're doing. If you have even a passing familiarity with programming, then you can probably figure out what the above does. It reads almost like psuedocode.
- Performance: Iterating with
LOOPis frequently quite fast. Expressions expand into highly imperative code that uses
SETQ. In other words, the extra language is worth the trouble to learn because it generates fast code with less typing. The above expression, for example, expands into roughly 70 lines of lower-level Lisp.
LOOP is typical of Common Lisp style as a whole. By "style" I do not mean what code looks like in your editor. Instead, by "style" I mean the way that Lispers approach problem solving, and the way that Lispers approach Lisp itself.
LOOP has lessons to teach about Lisp Style:
- Just to prove how good Lisp is for defining DSLs, one is included in the standard! From this I learned that sometimes a new language makes some domains easier to work in.
LOOP's limitations motivated many Lispers to extend the language, a very Lispy practice indeed. From this I learned that it is totally appropriate to extend Lisp to meet my needs.
LOOPprovides further evidence that Lisp is a truly multi-paradigm technology, one that that embraces practicality. That is,
LOOPmay stand out, it may seem impure, but it is a practical tool for implementing loops. From this I learned to stop worrying about purity in favor of doing what works.
Keep on hacking!