Unifying bitwise AND/OR with short circuit AND/OR


#1

With conditional short circuit operations for AND, OR, and XOR…we’ve struggled to find a good name for the bitwise operations. Previous thread on the topic: “Naming of bitwise OR, AND, XOR”

On the end of that thread I made a suggestion I want to bring up again, especially because we have a new option to make even the left hand side parameter convention change.

What if GROUP! on the right meant bitwise AND/OR/XOR?

>> #{FF00} and (#{FFFF})
== #{FF00} ;-- e.g. intersect #{FF00} #{FFFF}

>> #{FF00} and [#{FFFF}]
== #{FFFF} ;-- e.g. all [#{FF00} #{FFFF}]

There are competing needs in parameter convention on the left. Using the block form has come to need to let the left hand side complete, for instance to enable:

parse data rules or [...]

But the bitwise operators have historically acted like math ops.

add 1 2 or 3 => add 1 (2 or 3)

Doing both is now possible in the same function, due to “variadic left enfix”. This is what facilitates the SHOVE operator

The operator can start running, inspect its quoted right hand side for blockness or groupness, and then decide what convention to use when requesting the left argument. This request can then run an arbitrary amount of evaluation, before returning to let the right hand side run (or not run, if not necessary, in the BLOCK! right hand side case). Since the right side was quoted, this won’t run anything out of expected order.

Having the parameter convention vary is kind of a separate issue from whether the GROUP! form is bitwise. But it’s worth pointing out that it can be done now.

It would make the GROUP! semantics seem more consistent

Right now the GROUP! on the right merely forces the result to a LOGIC!. It’s still conditional and not bitwise.
And it’s still a short-circuit operation…that means the right hand clause might not run, based on the left hand side.

Yet in general, when you see a GROUP! as an argument to a function (that isn’t QUOTE) you expect it to execute. One thing about the bitwise operators is that they are not short-circuit, they always need to execute both sides to get a result.

Might this make it somewhat easy to explain? If you see a BLOCK! on the right hand side, it’s the short-circuit form and might not run that clause…if you see the GROUP! it will run for sure, it’s bitwise?

We can duck coming up with other names

I’m not 100% satisfied with and+ or or+. They aren’t terrible. But they aren’t stellar.

While it’s a new idea to be facing, I think and (…) vs. and […] are pretty easy to see the difference between. People will make mistakes, I’m sure. But of all the things in the language you might have to learn, the subtlety of “use a group if you mean bitwise” isn’t the biggest hurdle.

It’s weird, but what I kind of like about this “right hand side polymorphism” is that it’s a new and uniquely Rebol-enabled idea.

Thoughts?


#2

This is a worthy observation. But discussion produced a clearer way to utilize the difference.

Make the operation with GROUP! have the same return-value logic as the operation with BLOCK!, but have it be non-short-circuit.

>> (print "left" <left>) or (print "right" <right>)
left
right
== <left> ;-- runs both sides up front, but left result wins out

>> (print "left" <left>) or [print "right" <right>]
left
== <left> ;-- runs left side, doesn't need to run right for result

>> (print "left" false) or (print "right" <right>)
left
right
== <right> ;-- runs both sides up front, right result returned

>> (print "left" false) or [print "right" <right>]
left
right
== <right> ;-- runs left side first, result means it still has to run right

One benefit to this distinction is that there’s a way to make it clearer with xor that it has no choice but to run both sides in order to give an answer: simply don’t have a version of XOR that takes a BLOCK! on the right.

Enables something like Non-Short-Circuit ANY and ALL

I’d been wondering about making non-short-circuit variants of ANY and ALL…things that would run all their clauses, but not change their return value logic. This lets you build a chain of ANDs and ORs that gives you a choice at each step of whether you’re going to be short circuit or not.

 ;-- synonymous with `all [foo bar baz]`
 (foo) and [bar] and [baz]

 ;-- runs foo then bar always, but only runs baz if neither are truthy
 ;-- `all compose/only [quote (foo) quote (bar) baz]`
 (foo) and (bar) and [baz]

 ;-- `all compose/only [quote (foo) quote (bar) quote (baz)]`
 (foo) and (bar) and (baz)

I should point out they’re not precise synonyms, because ALL and ANY force failed results to NULL, making them usable with ELSE and THEN. AND and OR try to preserve the falsey state they’re given, so that:

 >> false and (true)
 == #[false] ;-- not forced to NULL the way `all [false true]` is

Notice also here you can mix and match, ANDs and ORs.

This alternative idea keeps that unique and Rebol-enabled feeling, without being as weird. It’s clever, but it’s also clear. Putting a block on the right hand side is unusual enough to make you ask “why is that in a BLOCK!” and the answer is “oh, so it will only execute it if it has to”…then that is the only difference.

Unfortunately it doesn’t solve the bitwise operation naming problem. For now, we’ll stick with and+, or+… and keep thinking…