Compatibility Idea: IS and ISN'T replacing == and !==, not = and <>

I think it's untenable that == and != are in the language, but not a pairing (the complement to == is !==). And I've said that == does not look literate, it's too much like a divider.

What I had proposed was having = take strict equality, and is taking lax equality. The wordishness of is made its fuzziness seem more appealing, and that appeal would be probably to the types of people who would like laxness in their comparisons. Those who prefer the symbol might (?) prefer strictness as a default. (This is a hypothesis, not tested.)

But having written Making the Case for Caselessness, I argue that case insensitivity has some legs. Maybe enough that we need not be ashamed to have = be a "lax" equality operator, in the sense of comparing case-insensitively...anyway.

Which raises a question. What if is and isn't were the strict equality operators, and =, !=, and <> remained lax?

It still kills off == and !==. And it's much more compatible with history. With this being used implicitly as the comparison operator in things like FIND and such, it can be argued that laxness takes primacy for the = symbol.

All things being equal (ha ha), I would probably prefer it the other way around. Math explains what "equality" is, and it doesn't have a "stricter" form. But "is-ness" is something more vague to define.

But there's code and expectations out there. So perhaps this is the way to go. Once you've taken == and !== out of the picture, I think it may become one of those too-close-to-break-even changes. And Beta/One philosophy is that we just stick to status quo in that case.

When I looked at the examples on chat, this way looks much better to me.

2 Likes

Looking at how it reads, is seems to imply an exact match more than =, i.e. "to be or not to be" that thing being compared to.

1 Like

It's hard to say whether I'd see it that way if I were looking at it from first principles or not.

Given that I can't really tell, the path of least resistance is to keep = lax, make is strict.

This has been tumbling around in my mind a while. And I guess it's a winner. The only gray area is where other languages have taken this to mean what same? means.

I prefer the prefix forms being something like is? and isn't? instead of strict-equal? and strict-unequal?. It seems clearer than trying to take same? and different? and figure out something else for SAME? (like ALIAS-OF?)

How lax should lax = be?

People keep wanting to say that 1 = 1.0 ... despite those being two different datatypes historically (vs. a unified NUMBER!). There has also been talk of letting "A" = first "abc", where a single character string is convenient to compare. (&A = first "abc" would be another possibility with my pet HTML entity for & proposal)

But even though I have made the case for caselessness, I am skeptical that = should be considering different casings of different type words/strings to be equal. foo: can equal Foo: and FOO:, but I am not pleased with:

rebol2>> (first [foo:]) = (first [:FOO])
== #[true]

It's a bit of a pain to have to canonize types to match for the cases when this is what you want to ask. Perhaps another concept that is not equality is needed here.

We could have x like y for this, and maybe it shortens to x ~ y for greater brevity. Then have UNLIKE and !~. So perhaps the only thing that is lax about plain EQUAL? is its tolerance for the human concept of casing?

Predicates may make it easy

If we remember the concept of predicates, you could get this pretty easily if you felt it was important:

>> find [a: 10 b: 20] 'b
; null

>> find [a: 10 b: 20] /like 'b
== [b: 20]

Remembering that these slots are hard literal <skip>-ables and only match PATH! (or whatever winds up being used), evaluations can defeat it (including quotes)

>> find [a: /like b: 20] '/like
== [/like b: 20]

So it could be LIKE that thinks of 1 and 1.0 being the same besides being different types, or FOO and fOo:.

And another year goes by, and the tumbling continued...

With the virtual binding change has come case-sensitive binding.

>> FOO: 10
>> foo: 20
>> FoO: 30

>> FOO
== 10

>> foo
== 20

>> FoO
== 30

These features wound up connected because WORD! cells only have so many bits in them. So I sacrificed "spelling-variation-bits" to give "virtual-binding-bits".

We should be cautious in using case-sensitivity as a "feature" until we understand it better, and if it's what we actually want. If you don't start leveraging this as a purposeful feature, we could roll it back. There are other options--like increasing cell size--which could be pursued.

But as I've explained, Case-Preservation and Case-Insensitivity are fundamentally at odds.

With binding being case-sensitive, this leads me back to my original feeling that case-insensitivity should likely be the is operator, and that = be the more "exactly equal" notion of case-sensitive equality.

>> 'Foo = 'foo
== #[false]

>> 'Foo = 'Foo
== #[true]

>> 'Foo is 'foo
== #[true]

>> 'Foo is 'Foo
== #[true]

My instinct is that the "I don't like having to be overly precise" nature of people who would want to gloss case, would go along with "I don't like math or code that looks like equations"...and that such a person would be happy typing IS and getting case-insensitivity with that.

Predicates Semantics: Harder Than Expected

Using predicates with things like FIND is a bit tricker than I thought because they work on arrays of items.

 >> find [a b c d] [b c]
 == [b c d]

If you're going to pass in a predicate to override the default, if that predicate can only compare two values against each other...that's a bit constraining. It means the operator doesn't ever see both the [b c] items in the comparison at once, it's only comparing the arrays member-wise.

Figuring out where to put the predicate is a bit confusing also:

 >> find .is [a B c d] [b c]
 == [B c d]

 >> find [a B c d] .is [b c]
 == [B c d]

 >> find/predicate [a B c d] [b c] :is
 == [b c d]

And /PREDICATE is a really long name for the refinement, which makes /WITH seem appealing to take for this purpose (though it's also a possibility for a binding operator, if IN is considered too good as a variable name)

 >> find/with [a B c d] [b c] :is
 == [B c d]

I liked the idea of having a unified name for predicates, but SORT historically used /COMPARE...

Anyway, I wanted to point out this still hasn't been resolved, and that the binding change would seem to point more toward the idea of case-sensitivity for the symbolic =.

In another frustrating epicycle of this problem...

I was working with what I'm starting to call the "core return protocol". In this protocol, all "normal" return values are QUOTED!...including NULL, which is represented by a lone apostrophe (') That allows special values like an empty block ([]) to convey invisibility, and NULL to convey a "true null" e.g. the operation failed.

This is especially nice because it means that parse combinators have a way to return a null indicating failure separately from a successful rule that just happens to return a null result. For example, the GROUP! as a rule (if false [print "not printed"]) doesn't cause the parse to mismatch...but it evaluates to NULL. Similarly when opt "A" does not match an A, it will keep going...but evaluates to null. Yet these cases return the quoted null (') and not plain NULL which indicates a failure.

But historical equality considers quoted things to be "equal" to the non quoted thing:

rebol2>> (first [a]) = (first ['a])
== true

r3-alpha>> (first [a]) = (first ['a])
== true

red>> (first [a]) = (first ['a])
== true

That means that when I tried to write a simple check that a return result wasn't "true null" I had a problem:

assert [result != null]

If the result is a quoted null, this matches...but I didn't want that. I pretty much would never want it.

Going to have to put my foot down here. If you want to compare things dequoted, then dequote them.

assert [null != dequote result]

(Of course, you may introduce your own operators that dequote...as Redbol will, but I think it's a mistake.)

1 Like