Lazy OR and AND

In the relatively recent past I’ve been again caught out by the eagerness of Scala’s | and & operators. How did I get caught out you might ask as I’ve known for a long time that Scala is eager by default. I think it was because it was in the context of match guards. If and If-else constructs are lazy even in C, so I guess that put me into a lazy frame of mind. The normal catch involves a collection. If the collection is non empty AND the head element conforms to some predicate, throws an exception on an empty collection.

| and & 's eagerness is irritating. So this got me thinking, is there any reason not to make them lazy? Is there even a single piece of Scala code out there that relies on | and & 's eagerness?

Note: Scala of course does not have lazy by value parameters, but in this case a by-name parameter is functionally equivalent to a lazy by value parameter.

1 Like

and they aren’t in scala? what do you mean by laziness here and how does that translate to | and & operators?

|| and && do short circuit in scala (Short-circuit evaluation - Wikipedia). | and & evaluate both sides. i belive it’s the same in java, c and other languages.

are you using | and & by mistake where you should be using || and &&?

6 Likes

It would be easier to help if you posted code instead of words, which are imprecise.

scala> true | false
val res5: Boolean = true
                                                                                                                                                                                  
scala> true & false
val res6: Boolean = false

Yes I am talking about short circuit evaluation although I wasn’t aware of the term. From the above linked Wiki

In any programming language that implements short-circuit evaluation, the expression x and y is equivalent to the conditional expression if x then y else x, and the expression x or y is equivalent to if x then x else y. In either case, x is only evaluated once.

Because of this I disagree with Dijkstra. The fact that exclusive or can not have a lazy 2nd parameter does not really complicate things in my opinion.

scala> var aa: Array[Int] = Array()
var aa: Array[Int] = Array()
                                                                                                                                                                                  
scala> aa match{
     | case _ if aa.nonEmpty & aa(0) > 0 => "Hurrah"
     | case _ => "Boo"
     | }
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

The eagerness of the second parameter to the AND method, requires an additional match to avoid the exception.

1 Like

It’s possibly a bad idea (due to this confusion) that | and & even work at all for Boolean, although it kind of makes sense. Those are the bitwise-comparison operators, not the logical ones, and it’s 100% intentional that both sides are evaluated: you’re combining all of the bits in the values in order to get the result.

That makes more sense if you think of the Boolean as a one-bit Integer – just as 32-bit Integer | needs to be eager, so is the one-bit Boolean. It would be semantically weird if the 1-bit version behaved differently from the 8-bit, 16-bit, 32-bit, etc versions. It’s explicitly documented as such.

| isn’t the same thing as ||; & isn’t the same thing as &&. They have different purposes and use cases. (And Scala isn’t particularly unusual in this, AFAIK.)

8 Likes

Right, because you should never, ever, use & for logical operations, only for bitwise ones. You should use && instead. It’s the wrong operator.

Ah OK understood!

Never had cause to check documentation before.

At some point, maybe a few years ago, I proposed swapping the semantics, so that ‘&’ is short-circuiting and ‘&&’ eager, but I don’t have a link, though I know I did not think it was practical. I think the context was interesting, but I do not recall what that was.

There is a ticket about practical concerns.

Some men really do just want to watch he world burn…

6 Likes

I haven’t watched the updated cover of “We didn’t start the fire” yet.

EDIT: it doesn’t mention boolean ops

FWIW, in the red book, the authors use && and || in their explanation of ‘Strict and non-strict functions’ and ‘by-name parameters’, saying that you can also think of the two boolean operators as functions that may choose not to evaluate their arguments

see first 6 slides in

Seems & and | on Booleans should trigger lint warnings, reminds me of

1 Like

I don’t know if I agree. Sure the behavior isn’t perfect, but it’s been a de-facto standard for literally 50 years now. Anyone coming to Scala from Java or Javascript - the two major platforms we run on - will likely be familiar with this. I feel that’s enough of a global standard of behavior that we don’t really need to add any Scala-specific guardrails here.

5 Likes

+1 to this.

Just because one user has got this wrong doesn’t mean that the solution is to add extra checks. I suspect that RichType now understands how this works, which will help for all C like languages.

Then again, legit use cases for & and | on booleans are rare; very rare. The only good reason I can think of is if you need to exploit their branchless nature. That would only make a difference in highly performance-sensitive code, where hopefully your code is so littered with comments justifying what you’re doing that an additional @nowarn won’t make a difference. I say that as someone who has actually written branchless “algorithms” in performance-sensitive code in Scala (with measurable outcomes!).

6 Likes

I don’t feel strongly about it, but there are also beginner programmers, not just programmers coming from other languages.

The only reason i can imagine to use & on a boolean is to avoid short-circuiting, but that’s so sublte that I wouldn’t want it in my code.

7 Likes

Yes luckily for me Seppuku, is not a strongly enforced norm in the Scala community. I did try and check in the REPL whether && was short circuiting, but somehow I messed up the test, concluding it behaved the same as &. Hence why it took so long for me to recheck and get it.

Personally I’m perfectly happy with how Scala handles this.

That’s a very flawed line of reasoning.

Only because someone did something in the past in some way and it didn’t change for a long time doesn’t mean that you should continue to do it in the same way!

With that “argumentation” you can just go back to program in C. Because “it’s been a de-facto standard for literally 50 years now”, so it must be fine…

Also a warning about a very likely bug doesn’t have any downsides. But a lot of upsides!

And just imagine, there are people who don’t “come form JS or Java”…

1 Like