[...]
Fair enough, James is becoming increasingly dogmatic, which is goading
me into responding.
Where am I becoming dogmatic? I actually posted an example of
a case where I do use multiple returns. And there are certainly
more important issues than multiple returns when it comes to
understanding code---I'd rather deal with multiple returns in
a ten line function with only only level of control nesting than
a 100 line function with 5 levels of control nesting, even if it
only has a single return. (Interestingly, my experience
suggests that the opposite is more common. People who write
short, easy to understand functions often---but not always---use
SESE. People who aren't bothered with loops spanning a hundred
lines, with five or more levels of nesting, generally aren't
bothered by throwing a return in somewhere in the middle of the
mess either.)
About the only statement I've made that could be considered
"dogmatic" is that SESE makes reasoning about program
correctness easier. This is a simple fact: it may be that it
doesn't have to be that way (and some more reason work has
tended to attenuate it somewhat), but all of the standard
literature on the subject describes how to reason about program
correctness based on a few standard flow control patterns, all
of which are SESE. If the function is simple enough, however,
the difference shouldn't be too significant for a human reader
(and there may be other reasons arguing against SESE); if it
isn't, the function is too complicated anyway, and needs to be
refactored. If various automated proofs are being used,
however, the difference may be significant; the proof engine may
not be able to handle anything but SESE.
I was probably too strong there, I should have said "By inspection
maybe". Checking preconditions and retuning early can make the code
easier to inspect and verify.
Which may, in some cases, be a valid reason for violating SESE.
Strictly speaking, any time you throw an exception, or have an
assertion failure, you violate SESE. Practically speaking, in
such cases, you're concerned with a much weaker set of
post-conditions---in the case of an exception, they still have
to be validated, but the usual validate procedure is simply to
ensure that an exception cannot occur when the "exceptional"
post-conditions are not met (e.g. an object has an inconsistent
internal state). This is not always trivial, but it does
require a different sort of reasoning than that used when
dealing with post-conditions along the lines of "the return
value will be accurate to one half LSB", for example. (The
latter, of course, is a good example of something that cannot be
tested in reasonable time. And which is also often very, very
difficult to prove by analysis as well.)