When I wrote poetry as a kid, I often made many (>20?) passes over the same paragraph, looking at:
- words
- sentence structure
- length of those things
- cadence
- rhyming scheme
- more I'm sure long forgotten
Depending on the kind of poetry I was writing, or the feel I was going for, it would take more or less passes. But the common theme that surfaced in these efforts for me was deleting words/sentences from the passage. Succinct was usually the net result. Inevitably, I had used more words in the first pass and it had only cluttered the text. The point got muddled in the words.
Haiku became interesting for that reason. Limericks too - in a different way. (Remember - this is kid poetry - although I have to admit that Haiku lives on for me...) It was a game of how to fit the most impact/knowledge into a structured form. A lot of the meaning came from the context of the reader, or the imagination of the reader to shift several contexts in a reading.
I continue on in these pursuits now, mostly out of habit, for I'm not sure what else they really net. And it has carried on to code now. In two ways:
- Form and brevity usually wins
- Shifting context is a feature
Take
Basho's famous frog poem. I didn't get it until I was learning Japanese, but there are several contexts wrapped up in the poem due to the way that Japanese handles plurals (or doesn't). It is important because Basho "was enlightened" at the moment of the poem. Now two simple readings of it net that either a frog jumped into a pond and the universe clicked for him, or a bazillion frogs jumped into one or more pond(s) and the shear noise and lunacy of the scenario made the universe pop for him. The interesting thing is the imagery that jumps into our heads depending on the reading, and the simplicity with which that happens. Shouldn't code do the same?
Consider something simple:
x=123
if [ $x ]; then
echo "X:$x"
fi
Now, here it is again:
x=123
if test $x; then
echo "X:$x"
fi
And again:
x=123
test $x && echo "X:$x"
Those passes are about readability and conveying meaning with as few words as possible. It is also about patterns - it depends a lot on what you are familiar reading. But that last one is simple enough that it doesn't take much to understand what is happening. No real syntax tricks, nothing going on really.
Now for the context:
test $x && echo "X:$x"
We'll get it from the environment - which is probably where it is coming from anyway in a case this simple. So, there are four lines compressed into one and not a whole lot of text. But, more than the regular code measures, there is something "poetic" about that process and the end result.
Now, that code is really useless, but with a few changes:
function debugPrint {
test "$debug" && echo `date`": $*." 2>&1
}
it is extremely practical. My biggest gripe with this is the fact that it now reads like line noise, but the fact that it is one line of noise and not three, relaxes me some. :)
function debugPrint {
test "$debug" &&
echo `date`": $*." 2>&1
}
That last one is even more interesting because due to the indentation levels, the eye tends to follow the tree and not the blocks. Interesting due to the fact that the parse will (most likely) convert it into a tree internally, so this gives the illusion that we are looking closer at the internal representation of the code than we really are. In fact:
(defun debug-print (debug &rest)
(if debug
(print (date) rest)))
pseudo code, but similar to the block structured shell. Which begs the question:
function debugPrint {
test "$debug" &&
echo `date`": $*." 2>&1 ; }
I'm not so sure about that. The Lisp closes the block much more naturally than the shell, but both preserve the left hand side indentation. I think I just went to far. It _is_ a block language after all. Better to preserve the block-fact, while messing with the syntax just enough to get a window on the internal tree. (Fighting the urge to reformat that block!)
And subtly stuck into all of this is the fact that whitespace IS important. If what you want to say is (in part) defined by what you don't say, then it is even more important. Four-char tabs versus Two-space tabs. The arguments are long on tab vs space, but four versus two? It keeps the screen from crawling right. Contrast the above blocks to some OO:
public static void debugPrint(int level,
String[] message) {
if (Environment.debug >= level) {
println message
}
}
Which is how you would write it if the argument list to the function was long. (
Alan Perlis). I don't see the tree, and the whitespace highlights the wrong thing. It is like the beat poetry class where the student tries to deliberately break form and "just go with the flow".
I've noticed this pattern several times in my OO code, the code "flows left". Many of the functions will tend to be long (heavy) lists down the right side of the screen, followed by procedural code on the left side of the screen. The net effect is something like a checkerboard or a left-waterfall. I find seasoned OO programmers have learned how to ignore this altogether, and others have IDE tricks that allow them to skip over it.
So we try:
public static void debugPrint(int level,
String[] message) {
if (Environment.debug >= level) {
println message
}
}
to try and bring the eye back to the tree. Does it work? It does hide the argument list a little.
public static void debugPrint(int level,
String[] message) {
if (Environment.debug >= level)
println message
}
Now the tree starts to come out a little more. But only by detaching the function declaration from the body.
public static void debugPrint(
int level,
String[] message
) {
if (Environment.debug >= level)
println message
}
There isn't really much that can be done to recover the tree. And because of that, the immediate response to the first pass reading it is that the brain can't pick out the patterns. I actually have to compile this stuff in my brain, rather than read it instinctively.
You know how easy it is to read a limerick? It just flows, you already know the meter and what to expect from each group. You can hear the measure in your head without having any words, but most of us remember all the words to at least one limerick. Compare that to the beat poets. You have to study Ginsberg to know how to read it. It doesn't happen the first time through (in many cases). These kinds of poetic structures are designed to shake the brain, force the reader to concentrate and parse it word by word. Limericks are designed for mass consumption.
It's about being able to look at a block of code and know instinctively where in the tree you actually are. I find this process easier to perform if the code at least resembles the tree that it parses into. Code is also for mass consumption - or it should be. It should be simple to grok what something does - its paramount to reuse. Code should be like The Man From Nantucket - not Howl. Perl Golf is like Howl - awesome when you spend time in it/with it. Not so hot if you want to reuse it.