A Programmer in King Arthur's Court

Occasional commentary about software, software construction, and the business of software.

Monday, October 12, 2009

A Cyclical Observation about Functional and Conventional Languages

Hmm. Ten months. That's a long time to go unpublished. I think I'll address a minor point in favor of conventional control structures and in opposition to the purest purity of functional-only languages such as Scheme. (I'm deliberately ignoring macros, and I'll say why later.)

So suppose you have in mind that a computer is going to do some work for you. Then despite any rhetoric or ideology, you're going to have to instruct the machine you have. Yes, we can argue until we're blue that we should instead make machines that follow our instructions, but that argument doesn't pay the bills.

Now, as I was contemplating compiler development, I bumped into this little notion of a "control flow graph". Nodes in the control flow graph are points in a process, edges are steps in the process, and cycles in the graph correspond to loops in the program.

A pure functional language won't offer explicit loops as a primitive concept. Instead, the programmer has to decide on some suitable places to break the loop into parts, then code the individual parts as separate functions with distinct names.

So consider the work done for you by the part of a compiler that builds a control flow graph. If you have loops as a primitive construct, then the compiler finds all the right places to take apart your loop, and you don't have the trouble of inventing names for parts of a cycle. In short, the compiler does some of the thinking for you. This is undoubtedly good, because the compiler is likely to get that thinking right a greater fraction of the time.

The functional faction decries the need for mutable state that a classical procedural loop introduces. Well, yes. Computer programs do have state, and that state changes from time to time, or else nothing would happen. The real conflict is about the assignment of new values to old names as a step in some process. Functionalists say that if you can do that, you've added avoidable complexity.

The fact is that calling a function assigns new values to old names. The old names we call "formal parameters" and the new values we call "actual parameters". There may be a certain ideological purity in only assigning coherent groups of new values as one unit. However, this produces its own intellectual overhead.

Now, I should mention the bit about macros. In Lisps of every sort, you can write Lisp code that writes Lisp code, then rely on that meta-code for the rest of your program. A typical example creates the appearance of a new control structure by automatically decomposing, say, a loop, into appropriate other primitive parts such as functions and decision points. When an established standard provides these macros, then they are morally indistinguishable from primitive structures themselves.

The intended fundamental benefit of a macro facility is to enable the bulk of the source code to have the kind of lexical relationships that correspond with the semantic relationships the programmer hopes to express. In other words, it grows the programming language toward the problem, bottom-up, specifically when the existing programming language is ill-suited to the semantics of the problem.

What actually happens with macros? Well, I'm not an avid user of Lisp or Lisp macros, so I can't say for certain, but the academic examples tend to cluster around premature optimization and the reinvention of do-while loops. I can imagine room for the generation, at compile-time, of otherwise-cumbersome data structures, and I can also imagine the generation of code from some other form of specification. For example, you could build a parser-generator out of Lisp macros.

Well, the thing is that people don't build parser-generators very often. When they do, it's usually an academic exercise. Far more often is to use one, and in that case it's plenty good enough to have one that works. The main point is that your creation is effectively documented for later use.

The creator of what became "Yahoo Stores" credits his success to the use of Lisp and its macros when everyone else was using C or C++ or perhaps Java. I suspect that someone using Perl::CGI would have had a near-equal advantage over such competitors. I can't imagine that his advantage came entirely from macros. In fact, I don't think macros really even helped, except to the extent that his compiler needed hints about what constants to fold for suitable execution speed. I suspect that the real advantages came from automatic garbage collection, sensible failure modes, terse grammar, server-side deployment, capable preexisting libraries, disciplined thinking, and a design committee of only one highly-talented and probably very educated person.

And finally, the widespread example of writing a macro to simulate a control structure indicates that the control structure is in fact missing! So therefore, all hail the explicit loop!

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home