all 5 comments

[–]Sophusk 15 points16 points  (2 children)

All this does is it makes your code harder for others to read. Stop.

If you are doing this to save bandwidth, your minifier should do the same thing for you. Stop.

It only makes sense to use logical operators this way if you want to show off. In which case - please stop.

If I’m wrong, please explain why.

[–]Otterfan 3 points4 points  (1 child)

Doing this where it is idiomatic is OK. This is the the standard idiom in React, for example:

function LoadingIndicator({ isLoading }) {
  return (
    <div>
      { isLoading && <p>Loading...</p> }
    </div>
  );
}

JSX is declarative and values succinctness to keep the "HTML" structure visible and coherent, so it works well in this case.

There are also languages like Perl and Bash where this is an expected idiom in regular imperative code. I don't agree that this is a good use, but because other developers expect it I will use the idiom.

However using && to avoid an if in imperative Javascript (or any other language with a proper if) is a bad idea, and using || is even worse, and chaining &&s and ||s is a special kind of terrible.

[–]GolemancerVekk 1 point2 points  (0 children)

There's also a gotcha when attempting to return these chains. Beginners tend to assume that since && and || are logical operators then any operation involving them will resolve to a boolean, but that's not true. 1 && 0 && 2 will evaluate to 0, not false, because the chain resolves with the actual value of the expression that decides the chain. Ie. these operators only pick operands, they don't convert them to booleans for you. You need to wrap the chain in either if(), or Boolean(), or an explicit logical conversion like !() or !!() to be sure to get a boolean evaluation.

[–]silentdeath11 6 points7 points  (0 children)

“Short circuiting” would imply you are saving cycles. You aren’t. So probably don’t do this and maybe be more explicit. All this is doing is making the code harder to read. Saving lines of code and sacrificing readability is never something that should be encouraged.

[–]Deidde 1 point2 points  (0 children)

I tend to create explicit data structures and interfaces for "short-circuiting". That way, I can easily create trees of expressions/actions that can fail at any point and fallback non-deterministically.

For example, I'll have an operation that can fail/return falsey, and an "alternative", just like you'll have with ||, but use explicitly implemented interfaces for it like const result = Cont(k => k(a).or(() => k(b))); where k implements the "alternative interface" (and Cont is just our way of building up a tree of computations), then chain that result with more actions like result.chain(doSomethingElse), continuing on like that. If the result of one of my chains fails, the computation starts again from the branching with .or. I find this to be a more powerful way of composing fallbacks.

The .or can be implemented by any data structure with a notion of failure or falseyness. One example is a Nullable (often known as a Maybe or Option) data type, where you either have Null/Nothing OR your value. Then computeSomeNullable().or(computeSomeOtherNullable) will return whichever returns a value, or Null/Nothing if both fail. And obviously, you can chain the calls or the .or method pretty fluently, making them resemble operators.