4 ms·
That's interesting. On the other hand, I've seen code go through great lengths to not do a double assign. I doubt it was easier to reason about at all. The cod
by P_I_Staker 4y ago
That's interesting. On the other hand, I've seen code go through great lengths to not do a double assign.
I doubt it was easier to reason about at all. The code set an error var on a number of if / else conditions. They then used a separate flag, so that a double write wouldn't be necessary. ie.
int flag = 0;
int err; // notice no initializer
if(stuff)
{
flag = 1;
err = 1;
}
else
{}
if(other_stuff)
{
flag = 1;
err = 2;
}
else
{}
if(flag)
{
LogError(err);
}
I follow the "avoid double write" convention too, but in some cases, it can become very difficult to reason whether a local is uninitialized; this is a pretty terrible class of bug, because you work with garbage data off of the stack.
Setting to zero at declaration makes it much clearer that it will never be uninitialized, but it's essentially meaningless and arguably a form of dead code.
Maybe someone will suggest a redesign? More functions, smaller functions? It seems that there's a strong (though unpopular) argument that you should use medium sized functions, and some teams insist on doing this. In some cases, it can be easier to find the content, verify specifications.
Edit: On second thought I can see we didn't avoid the double write, just did it in a different variable. So I don't understand what the author's point is, lol.
- bluefirebrand 4y agoWhen I have a flag that is bouncing through a bunch of conditionals only to return at the end, I prefer to change it to multiple return paths instead of multiple spots to modify the flag that is being returned. If you are doing a lot of work inside those branches it may be worth a refactor to simplify the branching logic.
- throwaway2037 4y agoYou wrote: <<multiple return paths>> From experience, I have never once heard anyone from higher-level languages (above C & C++) complain about "multiple return paths". That said, for C, I definitely understand the need for reduced return paths due to lack of auto-free / destructor mechanics.
- P_I_Staker 4y agoIt wasn't THAT bad, but still annoying to figure out; and I would argue that this is never definitive. At least by basic inspection it's so easy to set an error. The fact that it set off the static analyzer is very annoying too. Now I have to somehow debug the static analyzer, or just accept the risks. I've had some uncomfortable experiences assuming the analyzer is wrong and ignoring or suppressing it... theoretically, the static analysis will do a much better job than a human, so I can't help but doubt, and feel I missed something. Just as an aside, some teams try to avoid multiple return paths. I believe there's some studies indicating a higher incidence of bugs... for a solid code quality reason you could theoretically deviate.
- bluefirebrand 4y agoI've never heard arguments against multiple return paths from a bug standpoint. It's usually a performance optimisation standpoint. I don't really see how it could increase bugs over setting a flag. With a function that sets a flag there's always a risk that you change the flag accidentally after you already hit the value you want. Which is the same risk as returning earlier than you want, I suppose. I just find the multiple return paths easier to read, debug and understand, rather than stepping through to trace the value of the flag, you just figure out which return is firing. The more I write, the more I lean into immutability though. Bugs happen when values can change that shouldn't.
- Twisol 4y agoI've seen some imperative code in the wild that looks like this: bool go = true; if (go) doA(&go); if (go) doB(&go); if (go) doC(&go); As a functional thinker, this is "just" an encoding of the Maybe monad, so I actually quite like it. If you care about small efficiency wins, though, it's not great -- an early bail means checking all the remaining conditions. It's cute though! I'm generally pretty okay with similar patterns over accumulators, as long as it's clear that the purpose of that variable is to be an accumulator. If we're just overwriting a variable because we don't need the old value anymore, or something like that, I'm much less charitable. A big benefit of functional programming, for me, is to learn the safe roads as explicitly as possible, so that you can then identify (and use) them when they aren't so well signposted.
- deleted 4y ago[deleted]