Today, the boolean operators &&/Bool.and and ||/Bool.or are strict (they evaluate both arguments before evaluating). For example, foo {} && bar {} will call both foo {} and bar {} before testing whether both are true. This differs from the short-circuiting behavior in some languages, where if foo {} returns true, bar {} would not be evaluated.
The question for this channel is, should this behavior be preserved? Some upsides of it are
Some downsides of it are
bar {} is expensive to compute and foo {} && bar {} does not short-circuit, you probably want to rewrite this program as if foo {} then bar {} else Bool.false. A lack of short circuiting can also preclude tail-recursion optimization and make it non-obvious that it's not applied, as this PR's diffs of examples/* demonstrateSome options here are
&& and || with anything that's not a variable or a literal, so that foo {} && bar {} would be a warning, which can be resolved by restructuring the program to befooResult = foo {}
barResult = bar {}
or
if foo {} then bar {} else Bool.false
Keep the current behavior if you call Bool.and/Bool.or, but apply short-circuiting if you use && and ||
Something else?
What do folks think?
It sounds like the warning option would provide more reliable performance, expected behaviour, and improve people's understanding/awareness of how this could impact performance. So given Roc's goal of being fast and friendly I like the idea of making it a warning (option 2).
My gut feeling is use short circuiting for && and || to match user expectations coming from essentially all modern languages. I don't really see any downside to it.
I think a warning is only good for the first few times. Over the years it will become annoying. At that stage you perfectly understand the Roc semantics and you are using everything correctly but the compiler is still complaining about a mistake you're not making that someone could make because of other languages. And that breaks your CI. Annoying.
Short circuiting seems strictly better. What would be a believable scenario where it would cause a problem?
(Regarding "breaking CI": Roc returns an error code to the shell when there are warnings, so your build fails. In other words, you can't really ignore warnings.)
Brian Carroll said:
Short circuiting seems strictly better. What would be a believable scenario where it would cause a problem?
suppose Roc is the first language I learned, and I have code like:
answer = fn1 foo && fn2 bar
I have a dbg in fn2 and I can't understand why it's not running.
to be fair, this is something I suppose someone would learn pretty quickly!
If roc is their first language they probably learned && from the roc docs so we have control over how they learn it and can teach them about short circuiting there.
The real danger would be someone learning roc as their second language when the first happens to not use short circuiting :sweat_smile:
I think based on this discussion my preference would be:
Bool.and and Bool.or continue to be strict, just like every other function in the language (which means you can use them when you don't want to short-circuit)&& and || are no longer syntax sugar for Bool.and and Bool.or, but rather compile to short-circuiting "and" and "or" like in most languageswhat do others think of that idea?
Sounds reasonable to me :+1:
Last updated: Jun 16 2026 at 16:19 UTC