Functional Style and Multiple Returns

In his excellent "programming in the small" series, Ivan Moore suggests that demanding a single exit point for a function is an example of cargo cult programming. I wonder if favouring or disfavouring that idiom has any connection with how much exposure a programmer has had to functional programming. Ivan's example showing the multiple return style:
void foo(boolean condition) {
if(condition){
return 5;
}else{
return 6;
}
}
bears a great resemblence to the (much terser) equivalent in Scheme:
(define (foo boolean)
(if boolean 5 6))
Why is the Scheme version so short? Firstly, Scheme has no syntax to speak of so nothing much gets in the way of expressing the computational idea. And secondly, in the functional paradigm we program by writing expressions for the computer to evaluate, rather than a sequence of instructions for it to follow. A use of foo, would look like this:

(display (foo #t))


which would print out 5 since the name #t stands for the boolean constant true. display writes out the value of its arguments. The value of (foo #t) is the value of (if #t 5 6) and if evaluates to the value of its second argument if its first evaluates to true, and the value of its third argument otherwise. That's harder to explain that to show. Anyway, (if #t 5 6) evaluates to 5. Notice that there is no return statement anywhere. All Scheme code is built of expressions that evaluate to some value, so a return is implied wherever there is no further level of evaluation to proceed down to, in this case when evaluating the literal constant 5.

This kind of thinking about programs, as expressions that evaluate to the desired result, leads to a lot of interesting places, can be very valuable, and crops up all over the place. Most importantly, it is relatively easy to think about, which makes it easy to write correct code in thie style.

Structured vs. Functional?

Ivan suggests that the single exit point style is a cargo cult much like the goto-less style--and both belong to the so-called "structured programming" tradition. The father of that tradition is E. W. Dijkstra, who offered a method for thinking about the other kind of programming, the sequence-of-instructions form (as seen in C, C++, Java, C#, Pascal, Delphi, etc etc), which is excruciatingly hard. Its a sad irony that the imperative style of programming tends to be taught first when it is so much harder to get right.

It seems to me that to prefer the multiple exit point form is to tend towards the functional (that is, expression evaluating) style, and thus towards easy correctness. And this is largely because functional programs tend to go to the essence of the problem being solved.

It is possible to do the single return style in Scheme, since it is an impure language:
(define (foo boolean)
(let ((return 0))
(if boolean (set! return 5)
(set! return 6))
return))
Without explaining how all that works it's still fairly clear that a lot of mechanics has been introduced: some local storage, some extra expressions for updating that storage, explicitly returning the value. All of this has got nothing to do with the essential thing of choosing 5 or 6.

Why would a programmer place upon themsleves the burden of understanding and maintaining all that non-essential stuff? And yet so many do, by clinging to the imperative style (do this, and then do this, and then do this) of programming, which in fact requires all the further overhead of structured programming to be tractable.

2 comments:

Cliff L. Biffle said...

So, how does your perspective here apply to Smalltalk, as a sort of halfway point between Scheme and the Algol languages? That is, do you see a paradigm difference in these two fragments:

^condition ifTrue: [ 5 ] ifFalse: [ 6 ].

condition ifTrue: [ ^5 ] ifFalse: [ ^6 ].

(I prefer the latter, in terms of locality of eyeball-reference...I find it clearer. But I'm curious on your take.)

keithb said...

Cliff,
Firstly sorry for the delay in your comment making it out here.

I think I prefer the first form. This may be because I've never done enough Smalltalk to get really comfortable with explicit answers in blocks, which I always have to stop and think about, albeit briefly. Does that count as a paradigm difference? I don't know.

Interesting that you describe Smalltalk as half-way between Scheme and the Algols. I see what you mean, but I tend more to think of Smalltalk and Scheme as "duals" or "inversions through the origin" of each other (or some similarly bogus geometrical analogy). And I probably really mean Common Lisp, Scheme isn't really dynamic enough to be that closely realted to Smalltalk, much as I love it.