Don’t LOOP when you can MAPCAR et al.
In Paradigms of Artificial Intelligence Programming: Case studies in Common Lisp (PAIP), Peter Norvig gives the following six maxims of good style:
- Be specific.
- Use abstractions.
- Be concise.
- Use the provided tools.
- Don’t be obscure.
- Be consistent.
Now consider something like:
(mapcar (lambda (...) ...) list)
versus
(loop for elt in list
collect ...)
Well, if we accept Norvig’s maxims, then the former approach is superior.
It’s specific – instead of a general LOOP form, it’s a MAPCAR and you immediately know what the purpose of the form is.
MAPCAR abstracts the transformation of a list (or several) into another. LOOP is a DSL for iteration in general. If the task at hand is the transformation of a list into another, or finding an element in a sequence, those are simple tasks with their own abstractions.
A task-specific abstraction is more concise than some general inlined approach. In the case of MAPCAR, the single atom MAPCAR indicates the list transformation intent. In the LOOP version, the three atoms LOOP, IN, and COLLECT together express the same intent (coupled with the absence of other things happening in the LOOP). Even having to specify LAMBDA, you still save an atom. Of course, task-specific abstractions mostly make sense when the task happens more than once.
MAPCAR makes use of the Common Lisp language for a specific task. LOOP is a very useful DSL for iteration. Re-implementing built-in functions through inline LOOPs means you’re a human compiler who compiles high-level tasks into a low-level iteration language. Low-level? Well, it’s low-level relative to MAPCAR et al. that abstract away the implementation details for those tasks.
Neither MAPCAR nor LOOP is obscure. For a frolic through plausibly obscure features (though many of them aren’t!), this is a fun list.
I suppose consistency is the only maxim LOOP could plausibly claim to win at – using LOOP for all iteration would certainly be a consistent approach. But given the above victories in MAPCAR et al.’s favour, the consistency of a low-level approach is only a consolation prize. Besides, using MAPCAR et al. and other custom abstractions where approach, and resorting to LOOP for specific complex iteration is also a consistent approach. One in which the presence of LOOP would correctly signal some general iteration, rather than mask the occurrence of common tasks.