Unions

Hi,

When I started programming this made a lot of sense to me...

if (foo && bar && baz === 1) {
  // ...
}

At first glance, we read it as "if foo and bar and baz are 1 then do this". Unfortunately, that's not how things work.

To implement this logic we need to do...

if (foo === 1 && bar === 1 && baz === 1) {
  // ...
}

I propose a way to do something like the first example using what I call "unions".

Logical AND Union

foo ~& bar ~& baz === 1

Logical OR Union

foo ~| bar ~| baz === 1

Side Notes

  • Unions can't be used as stand-alone expressions, they can only get along with comparison operators.

  • Unions are just syntactic sugars over my second example.

  • I used ~& instead of just & because who-knows-why they're valid JS.

Let me know what you think :blush:

1 Like

I agree - that's certainly an easy place for beginners to trip up on.

I'll point out another possible solution.

For the "~|" syntax, the python language does this instead, which I know some people like:

1 in [foo, bar, baz]

Currently, in JavaScript, you can do [foo, bar, baz].includes(1) to get similar behavior.

I remember seeing another thread floating around, proposing to add an "of" operator that's pretty much a shorthand for .includes() - but I can't remember where it is.

None of this really helps to solve the "~&" case. For that scenario, it might feel more natural to be able to write something like a === b === c - this is also a syntax with python supports - it's too late to support that in Javascript though, because it already has a different meaning.

2 Likes

Is there another language where comparisons work this way that informed your expectation?

The similar behavior would be [foo, bar, baz].every(x => x === 1), to ensure that all three of them were 1.

2 Likes

English :)

From my experience tutoring people who are just starting out, this is actually a common source of confusion. I've seen many people try to do exactly what the O.P. described to check if a specific element is X or Y.

When they try to do that, they just need to be taught to decompose their condition - instead of doing "a === b || c", they need to do "a === b || a === c". I'm not sure how much it would help to add a new ~| operator - it's still very possible that we go through the same process. They try to do "a === b || c", and they've got to learn to switch to the "~|" operator in this specific case.

1 Like

ha, fair, but i meant programming language :-p

In other words, I agree this is an assumption from natural language, but if it's one that every programmer has had to learn, then it'd be great to look into whether other languages have tried to mitigate this specific issue or not. If not, it's a strong argument that such mitigation isn't needed; if so, that might help inform what to consider for JS.

3 Likes

Well, I don't know of any programming languages that try to solve this.

I also think it's not an easy problem to solve (unless you do introduce two separate operators). This is simply because in English, "or" can really mean two different things, and what it means depends on context. Programming language can't be sensitive to context the same way (or, at least they shouldn't try to magically understand context).

So, perhaps that's why no languages (that I know of) have tried to hard to resolve this. The closet thing I can think of is the python "in" operator and the fact that you're allowed to do "x == y == z". Both of those features provide syntax to use the other form of "and" and "or" that languages don't directly support, without literally creating a second "and" and "or" operator.

It's too late to do the equal thing in JavaScript, but we certainly could add x of ['a', 'b'] if we wanted to (I don't have strong feelings for or against syntax like that). That sort of syntax provides a more natural outlet to express one's self, in a way that's closer to English. But, it's not like ['a', 'b'].includes('x') doesn't read like English either.

1 Like

Perl 6 has "junctions", which (if I've got this right) allow you to say (e.g.)

if all($foo, $bar, $baz) == 1 {...}

or maybe even

if $foo & $bar & $baz == 1 {...}

Huh. Interesting.

I think that first scenario might actually be possible to recreate in userland with the operator overloading proposal. Provide an all() function that returns a special object that has === overloaded on it.

Pretty sure equality can’t ever be overrideable, even in that proposal.

However, you could return a distinct symbol or object whenever all three weren’t equivalent, ensuring that comparing to it would always fail unless all three were the same.

Ah, you're right. === can't be overloaded, but == can.

I like Python’s approach a lot.

a == b == c

Means they all equal each other, it’s not 2 binary expressions that return booleans

1 Like

Could this also be expanded to less-than/greater than? Perhaps something along the lines of:

if ( 0 ~< 1 ~< 2 ) // true

In Python, you can just do 0 < 1 < 2 and it just works. I was thinking about something similar that would work with JS.

1 Like

Me too.

@theScottyJam, I didn't mean this just for beginners. I feel there are real use cases for this, where we can introduce this syntax to improve readability.

We can use the logical OR when we need to...

  • validate multiple choice fields in a form,
  • do union type checking

And logical AND when we need to...

  • to check if a participant has a reserved name in a meeting (if we know the reserved name ahead of time, of course)

Actually, I've found that we can achieve the same thing as my first example using this.

(foo & bar & baz) === 1

I like the "of" thingy. But I feel unions are more powerful, as we are not limited to using just ===. We can use absolutely any comparison operator. Like here...

foo ~| bar > 18

We could use some instead of this (every for ~&). But I find them more verbose.

[foo, bar].some((i) => i > 5)

There are places where we can only use "of", like...

if (username of usernames) print("Reserved :(")

So I think both proposals can co-exist, and serve different purposes :)

It looks like a good idea, but that would be a totally different syntax. Also, if we were to allow that, we'll need to do the same for all the other comparison operators. Besides unions can only be used for logical operators. They are used in combo with comparison operators.

I find the examples with ~| and ~& a bit confusing. It's subjective, but the ~| evokes some kind of operation/relation between foo and bar, which is not the case, you're treating them independently.
I'd be more comfortable with a wordy syntax, like

if (all of (foo, bar) > 18) ...
if (any of (foo, bar) < 21) ...

(this imo better conveys the values are independent, and that the whole thing can short-circuit)

1 Like

How about a syntax like in Julia:
[foo, bar, baz] .=== 1

1 Like

@jithujoshyjy, I don't know Julia much, so, does it behave like ~| or ~&?

There are these really interesting things called vectorized operators in Julia:
syntax:
.<operator>
eg:
.+
.-
.*
.etc
They apply the operation on every element of an array.
so [foo, bar, baz] .=== 1
[foo === 1, bar === 1, baz === 1]
might not be what you've asked for; I just wanted to drop this in.

1 Like

That could work, but then we'd be left with an array of booleans, which we would still need to consolidate into a single boolean by doing .every() or .some() afterwards.

e.g. it would look like this:

([foo, bar, baz] .=== 1).every(x => x)

Which isn't really better from this:

[foo, bar, baz].every(x => x === 1)
3 Likes