6 ms·
> The named booleans suggested here help adding clarity, but more often than not they'll end up being single-use (const) variables that just paraphrase their de
by simplotek 4y ago
> The named booleans suggested here help adding clarity, but more often than not they'll end up being single-use (const) variables that just paraphrase their definitions.
That's ok. The worst tradeoff in the "named booleans" pattern proposed in the blog post is that it misses the whole point of chaining predicate evaluations, which is leveraging short-circuiting to prevent further predicates from being evaluated.
Chaining logical comparisons and the way they short-circuit is specially important when a predicate has preconditions and/or is expensive and/or has side-effects.
If your predicates are hard to read you don't fix that mess with "named booleans". You fix them by extracting them to a pure function like
isNotUnknown(_someLongNamedVar) && isNotEmpty(_someLongNamedVar)
- toxik 4y agoNest the if statements then, perhaps in an else branch. Relying on short circuiting to avoid side effects or improving performance meaningfully is pretty shaky.
- BeeOnRope 4y ago> Relying on short circuiting to avoid side effects or improving performance meaningfully is pretty shaky. Says who? IME relying on short circuiting is useful and idiomatic in C and C++ code, e.g.: if (callback && callback->run()) { ... } In your opinion would it be better to write: if (callback) { if (callback->run()) { .. } } The situation is even more awkward if you try to avoid || short-circuiting, since you need to duplicate the body of the conditional.
- DubiousPusher 4y agoI tend to agree with toxic here. Side effects inside conditionals should be avoided, in short circuit conditionals even more so. In a case like this, my preference would be to give callback a dummy value and always run it. I subscribe pretty heavily to the Carmack philosophy of always running the code as similarity as possible and making a decision at the end of a procedure.
- simplotek 4y ago> in short circuit conditionals even more so. I'm curious, what leads you to believe that skipping unnecessary calls to functions with side effects is something to be avoided? Do you believe that unconditionally calling code with side effects specially when you don't have to is something to be desired?
- DubiousPusher 4y agoI believe that running the same code path as much as possible per software cycle is to be desired. The code I'd call in the case that client code didn't specify a function wouldn't produce any side effects. It would be an empty function object/pointer. I would almost always rather support default behavior through dummy data rather than null checks. I find that a lot of software bugs come from the unintended consequences of branching. Especially branches which create state that is consumed later. In fact, I find that most null checks lead to bad software behavior as it often hides bugs. Early outting when null is detected is especially silly to me. The client has just tried to run a procedure which had to be abandoned due to a lack of data and you're just going to quit silently? If you're using dummy data for default scenarios and asserting not null rather than checking, you will eliminate most logical null checks in your code. Though I do concede that in the case where you must support null checks, such a short circuit as above is acceptable and in fact preferable to me over two if statements.
- BeeOnRope 4y agoI think side effect is a red-herring here: it's idiomatic to rely on short circuiting when the RHS relies on the LHS being true to be safe to call at all. In the above example, we check that the pointer is non-null before calling it. The "side effect" we are avoiding is a segfault. In the code that splits the conditional into two, the callback is still run inside a conditional, just by itself.
- jcelerier 4y ago> In your opinion would it be better to write: in mine, yes definitely 100% the second code is more readable than the first
- simplotek 4y ago> in mine, yes definitely 100% the second code is more readable than the first If anything, adding a nesting level for each chained predicate makes code harder to read, and needlessly so.
- nuancebydefault 4y agoIt makes code a tiny bit more verbose but much more readible and most of all easily extendible without having to worry about bracket positions
- simplotek 4y ago> It makes code a tiny bit more verbose (...) The issue is not verbosity. It's the needless addition of nesting levels and independent expressions in a non-idiomatic way, which greatly increase the cognitive load of checking a single row of chained predicates. It's bad code.
- jcelerier 4y agofor me the cognitive load of if(an expression) { if(another expression) { } } is MUCH lower than if(an expression && another expression) { } which always raise the questions of: - did the author think about operator precedence - did the author want to use && or & - did the author think about short-circuiting (as evidenced in this very thread, a lot of people do not)
- nuancebydefault 4y agoAnd not to forget, is it okay to omit parentheses, does && take precedence over any other symbol in the expression?
- toxik 4y agoI subscribe to the idea that your code should state your intent as closely as possible. I question the idea that you would ever want the very linchpin of your function hidden away as a second subclause of an if statement. A function that calls callbacks is probably mainly doing that, let it stand proud and clear in the code text. If your function is calling callbacks left and right “on its way somewhere else”, I think a refactor is in order. Maybe you will now present some second scenario with something other than callbacks. Maybe in that case you’ll have me convinced it’s an okay exception. For you see, I said only it’s “pretty shaky”, not a mortal sin. With that said though, I think in 9/10 cases, it’s something you can solve with an early return. if (!callback) return; callback();
- simplotek 4y ago> Relying on short circuiting to avoid side effects or improving performance meaningfully is pretty shaky. You've got it all backwards. Short-circuiting is not used to avoid side effects. Short-circuiting is used to avoid needless calls to any predicate, which can have and often do has side-effects, and if it doesn't have today it might have tomorrow. If you don't call them when you don't have to, your code is in a better shape. And let's not pretend that unconditionally macode with side effects when you don't have to is a good practice.
- juiiiced 4y agoI usually prefer to write more readable code, and then optimize later if necessary. If the short circuit becomes critical for performance then I would leave a comment, or as the parent suggests just order it with an early return.
- simplotek 4y ago> If the short circuit becomes critical for performance the The point is not performance. The point is readability, and expressing things clearly and with the appropriate level of detail and leaving out nesting and scope. Safety and performance are important but secondary advantages.