Many programmers insist on only having the flow of control in their function end at a single point. I got wondering for a few minutes if any languages enforce this style of programming. Then I realized. Erlang has no 'return' operator, so doesn't Erlang enforce this? That sounds about right as far as I can see. So an Erlang function can only have 1 exit point. That should make them feel good.
But as was just pointed out to me in the middle of writing this, Erlang has 'throw'. There can be many of thoughs. So perhaps Erlang doesn't have 1 exit point.
On a final note:
Does having 1 exit point even matter? Who cares? The beauty of Erlang (in my opinion) is it almost forces you to write short concise functions. Every single one of my erlang functions fits on 1 screen easily, so does having multiple exit locations from there matter? In my opinion, no. I don't need to go searching through pages of code for that function to find it, I can see it, so who cares?
Well, I think the "single exit point" assertion is related to structured programming discussions and "GOTO considered harmful". It's a quite extreme opinion with which I do not fully agree.
ReplyDeleteFor instance, consider the following two pseudo-code snippets:
fun f1(...) do
if (...) then
r := ...
else if (...) then
r := ...
else
r := ...
end if
return ...
end fun
fun f2(...) do
if (...) then
return ...
else if (...) then
return ...
else
return ...
end if
end fun
Why is one better than the other (as postulated by the single-exit metric)?
Or, more extreme, if my language permits GOTOs I could even trivially transform every multi-exit program into a single-exit program.
Having deeply nested or page-long conditionals is neither desirable, and they can be avoided with multiple exits, for example (also with refactoring).
Another useful case of multiple exits is the simulation of contracts in a language which doesn't support them. Often, I write functions introducing data from the "outside" which look like this:
fun contract_example(...) do
if (!check1) return ... end if
if (!check2) return ... end if
...
return ...
end fun
Regarding functional languages, the debate usually does not come up there, because programming patterns are different. Recursion is used more often, and there a base case would be considered an "exit point",when the recursion is not going one level deeper:
fun contract (...) when check1 = ...
fun contract (...) when check2 = ...
fun contract (...) = ...
Such code is often very nice to reason about with induction because the base cases stand out, but so do they in the examples I gave above.
In fact, striving for code which is easy to reason about might be one of the causes why buzz-phrases like no GOTOs, single-exit, etc., come up, and then cause a lot of confusion.
So, I guess my (unspectacular) conclusion is that you can write "reason-able" code with or without multiple exits and the single-exit metric is not meaningful in itself.
I do agree that code containing exits (return, exceptions, etc.) all over the place in an unstructured way should not be written, but the emphasis here is unstructured. The reason is clearly that it is hard to assess correctness of arbitrary programs, and correctness
should be top priority.
Related discussion:
ReplyDeleteMultiple Exit Points at Joel's.
I wrote a short response, you can find it here.
ReplyDeleteI did get a bit off topic, and I don't think I really had to sell you on Erlang/OTP ;)