An obvious criticism of my previous post
SKILL
for the Skilled: Making Programs Clear and Concise is
that clarity is subjective. What is clear to one person may be
confusing to someone else, especially to someone who is accustomed to
doing things the hard way.
I'd also suggest the converse is also true. If you are
accustomed to using a programming language that encourages an
imperative style, you may become convinced that more confusing code
is clear.
Several readers of this blog referred me back to an iconic author in
the field of computer science, Peter
Norvig. The book Paradigms
of Artificial Intelligence Programming is a good resource as a
survey of many different non-trivial but easy to understand
programming problems. The book is almost 1,000 pages and belongs on
every SKILL programmer's bookshelf. The name of the book is generally
agreed to be misleading as it turns out not to be much about
artificial intelligence, but rather about programming style.
Norvig also presents a nice test for clarity in his presentation
called a Tutorial on Good
Lisp Programming Style. The rule can be found on pages 53 and 54
of that set of slides.
Rule of English Translation
- Start with an English description of the algorithm.
- Write the code from the description.
- Translate the code back into English.
- Compare 3 to 1.
From this description the implication seems to be that the smaller
the difference detected when comparing 1 and 3, the better job you have
done at writing a clear and concise program.
Let's use SKILL as an example rather than Common Lisp as Norvig does.
Suppose we want to write a SKILL program to do some analysis of the
instances in a layout cellView.
Start with an English description of the algorithm
Given a list of shapes and a layer name, find the subset of shapes on
that layer.
First attempted implementation
The following implementation is an imperative approach. The reader
sees how the calculation occurs but has to think a while to figure out
exactly what is being calculated.
(defun shapes_on_layer (shapes layerName)
(let (stack)
(foreach shape shapes
(when (shape~>layerName == layerName)
(push shape stack)))
stack))
Translate the code back into English
Given a list of shapes and a layer name, start by initializing an
empty stack, iterate over the shapes. If the shape is on the given
layer, push it onto a stack; when finished iterating return the
elements on the stack.
More clear and concise implementation
This implementation reads very easily, and it is clear to the reader
what is being calculated.
(defun shapes_on_layer (shapes layerName)
(setof shape shapes
(shape~>layerName == layerName)))
Translate the code back into English
Given a list of shapes and a layer name, return the set of shapes which
are on the given layer.
We can see that when translated back to English, the second of the two
implementations looks much more like the original problem statement.
Exceptions
Of course there are exceptions to every rule (except perhaps this
one). Sometimes we must sacrifice clarity for efficiency--particularly
if the programming language is not as high level as SKILL. If you
find the programming language you are using tends to more often force
you to write code in the style of the first two examples, rather than
the second, you should conclude that the strength of your programming
language is NOT its clarity and expressiveness.
Incidentally, I tested the two implementations above on my Linux
laptop to see which one is actually faster. I tested both the
imperative and functional style implementations on a cellView
containing 32,768 shapes, 20,480 of which are on "Poly" layer. The more
concise functional style implementation found the "Poly" subset about
7% faster than the imperative style implementation. In this case
there was no run time performance penalty for clarity.
The SKILL programming language allows you to write very expressive
code.
See Also:
Jim Newton for Team SKILL