Steve Wellens had a recent blog post arguing for the use of a goto in C# (see: Why goto Still Exists in C#). Steve had a series of methods he wants to execute, but he wants to stop if any given method returns false. At the end of the post, Steve decided that the following code with goto was better than setting boolean variables:
// DoProcess using goto void DoProcess3() { LOG("DoProcess Started..."); if (Step1() == false) goto EXIT; if (Step2() == false) goto EXIT; if (Step3() == false) goto EXIT; if (Step4() == false) goto EXIT; if (Step5() == false) goto EXIT; EXIT: LOG("DoProcess Finished"); }
In the comments, a remark from a different Steve stood out. Steve suggested using an array of Func<bool>. The comment didn’t generate any further discussion, but it’s worthy to call out.
I don’t think the problem with the above code is with the goto per-se. I think the problem is how the code conflates “what to do” with “how to do it”. In this scenario both are a little bit tricky. Let’s assume we might need to add, remove, or change the order of the method calls. But, the method calls are so intertwined with conditional checks and goto statements that it obscures the process. Using an array of Func<bool> is a simple approach, yet it still manages to create a data structure that isolates “what to do”.
void Process() { Func<bool>[] steps = { Step1, Step2, Step3, Step4, Step5 }; ExecuteStepsUntilFirstFailure(steps); }
You could argue that all this code does is push the problem of “how to do it” further down the stack. That’s true, but we’ve still managed to separate “what” from “how”, and that’s a big win for maintaining this code. The simplest thing that could possibly work for “how” would be:
void ExecuteStepsUntilFirstFailure(IEnumerable<Func<bool>> steps) { steps.All(step => step() == true); }
The All operator is documented as stopping as soon as a result can be determined, so the above code is equivalent to the following:
void ExecuteStepsUntilFirstFailure(IEnumerable<Func<bool>> steps) { foreach (var step in steps) { if (step() == false) { break; } }
}
With this approach it’s easy to change the order of the steps, or to add steps and delete steps, without worrying about missing a goto or conditional check. The next step up in complexity (excuse the pun) would be to create a Step class and encapsulate the Func with other metadata and state. I’m sure you could also imagine the execution phase relying on an IStepExector interface as the base for executing steps under a transaction, or with step-level logging, or even in parallel – and all this without changing how the steps are arranged. Take this to an extreme, and you’ll have a technology like Windows Workflow Foundation. :)
The ability of functional and declarative programming to separate the “what” and the “how” is powerful, but you don’t need a new language, and you can start simple. In this scenario it’s another tool you can use to save your city from the goto-zilla monster.
What do you think?