Strict Relational Operators

Michael J. Ryan tracker1 at gmail.com
Wed Apr 12 15:49:48 UTC 2017


That's part of why I suggested it... My mention of Object.* Was mainly that
it could defer to a common base class/constructor implementation for
comparison.  And that a string and number implementation should be
provided...

I'm also good with having non-matching types return undefined while
matching types is a Boolean.

Object.* could just defer to the prototype implementation of the first
value.. null or undefined always returning undefined.

-- 
Michael J. Ryan - tracker1 at gmail.com - http://tracker1.info

Please excuse grammar errors and typos, as this message was sent from my
phone.

On Apr 12, 2017 7:04 AM, "Darien Valentine" <valentinium at gmail.com> wrote:

> > Personally I think `a < b` should just become a compile error if the
> types are not number.
>
> Breaking stuff aside, I think this is an important point. The fact that
> the LT/GT operators do work on strings is a source of bugs. As with default
> sort, I’ve seen code written a number of times where it was evident the
> author expected the behavior would be more like `Intl.Collator.prototype.
> compare`.
>
> Unless I’m missing some important common use case for comparing strings
> based on byte values (`assert('a' > 'B')`), I think `Number.gt`,
> `Number.gte`, `Number.lt`, `Number.lte` would be a good solution.
>
> On Wed, Apr 12, 2017 at 5:09 AM, T.J. Crowder <
> tj.crowder at farsightsoftware.com> wrote:
>
>> > Personally I think `a < b` should just become a compile error if the
>> types are not number. TypeScript?
>>
>> I'm not following you. JavaScript variables don't have types, so it can't
>> become a compile-time error; and making it one would *massively* break the
>> web. (Yes, you can use TypeScript to get types if you like, but we're not
>> talking about TypeScript.)
>>
>> > ...that's a separable concern which should not be part of the
>> operator's behaviour IMO...
>>
>> There's no talk of changing how `<` and `>` (or `<=` and `>=`) work.
>>
>> But just as we have `==` (loose, coercing) and `===` (strict,
>> non-coercing), the discussion is about having strict non-coercing versions
>> of `<`, `>`, `<=`, and `>=`.
>>
>> -- T.J. Crowder
>>
>>
>> On Wed, Apr 12, 2017 at 10:00 AM, Alexander Jones <alex at weej.com> wrote:
>>
>>> Personally I think `a < b` should just become a compile error if the
>>> types are not number. TypeScript?
>>>
>>> If you want to also check that they are both number (that's surely what
>>> we mean here and not that they are both string!) and return `false` if not,
>>> that's a separable concern which should not be part of the operator's
>>> behaviour IMO. It would appear to just mask fundamental typing errors,
>>> unless I am missing some perspective?
>>>
>>> Alex
>>>
>>> On Wed, 12 Apr 2017 at 09:02, T.J. Crowder <
>>> tj.crowder at farsightsoftware.com> wrote:
>>>
>>>> Grr, there's always something. I forgot to mention that solving this
>>>> with functions is an option because short-circuiting isn't an issue, both
>>>> operands have to be evaluated by these relational operators anyway. So
>>>> unlike the motiviations for infix functions or macros or whatever, we don't
>>>> have that issue here.
>>>>
>>>> -- T.J. Crowder
>>>>
>>>>
>>>> On Wed, Apr 12, 2017 at 8:56 AM, T.J. Crowder <
>>>> tj.crowder at farsightsoftware.com> wrote:
>>>>
>>>> Very interesting stuff so far.
>>>>
>>>> My take on some options, organized into sections:
>>>>
>>>> * Solving it in userland
>>>> * Using symbolic operators
>>>> * Using functions
>>>> * Using non-symbolic operators
>>>>
>>>> # Solving it in userland:
>>>>
>>>> Anyone wanting strict relational operators today can readily give
>>>> themselves functions for it:
>>>>
>>>> ```js
>>>> const lt = (a, b) => typeof a === typeof b && a < b;
>>>> ```
>>>>
>>>> Usage:
>>>>
>>>> ```js
>>>> if (lt(a, b)) {
>>>>     // ...
>>>> }
>>>> ```
>>>>
>>>> So the question is whether the value of having a standard way of
>>>> expressing this is worth the cost of adding it?
>>>>
>>>> Playing into that is that various options come with varying costs:
>>>>
>>>> * Using symbolic operators has a cost, but doesn't change fundamentals;
>>>> I'm not an implementer, but I'd think the cost would be fairly low (but
>>>> non-zero). *Any* syntax change rattles parsing cages everywhere, but syntax
>>>> changes are now fairly regular occurrences in JavaScript.
>>>> * Using functions means no new syntax, which means not rattling parsing
>>>> cages, and are polyfillable.
>>>> * Using non-symbolic operators rattles cages, and probably more
>>>> significantly than new symbolic ones, and has been rejected in the past
>>>> (`is`/`isnt`).
>>>>
>>>> So that's in the mix.
>>>>
>>>> # Using symbolic operators:
>>>>
>>>> ## Form
>>>>
>>>> The closest I can come to consistency with `==`/`===` and `!=`/`!==` is:
>>>>
>>>>     Loose    Strict
>>>>      ==       ===
>>>>      !=       !==
>>>>      <        <=<
>>>>      >        >=>
>>>>      <=       <==
>>>>      >=       >==
>>>>
>>>> We can think of the `=` in the middle as being what signifies the
>>>> strict type aspect. The second `<` and `>` on `<=<` and `>=>` are a hack,
>>>> but a reasonable hack that's in the spirit of the original two strict
>>>> operators. :-)
>>>>
>>>> ## Semantics
>>>>
>>>> Because they're like `!==` and `===`, their semantics would have to be
>>>> in line with `!==` and `===`: The result is `true` if the operands are of
>>>> the same type and the relation is true, `false` otherwise.
>>>>
>>>> # Using functions:
>>>>
>>>> ## Form
>>>>
>>>> Given `Object.is(value1, value2)` there's an argument for putting these
>>>> on `Object` as well. But `Object` is an odd place for them (and indeed for
>>>> `is`). Perhaps we need a place for these to go. But sticking with `Object`
>>>> for now:
>>>>
>>>> ```js
>>>> Object.lt(value1, value2)
>>>> Object.gt(value1, value2)
>>>> Object.lte(value1, value2)
>>>> Object.gte(value1, value2)
>>>> ```
>>>>
>>>> So:
>>>>
>>>> ```js
>>>> if (Object.lt(a, b)) {
>>>>     // ...
>>>> }
>>>> ```
>>>>
>>>> Verbose, and again, using `Object` if I'm comparing numbers or strings
>>>> seems wrong. But it's consistent with the prior practice of `Object.is`.
>>>>
>>>> Michael J. Ryan suggested putting them on `Number`, `String`, and
>>>> `Object` instead, on the theory that if you're being strict, you know what
>>>> you're comparing. I'm not sure I agree that you do (a generic "take the
>>>> lower of these two" function, for instance), but there's something there.
>>>> It doesn't address the verbosity issue. (Presumably we only need `Number`
>>>> and `String` though, unless we're going to introduce a whole mechanism for
>>>> relational comparison of objects. Or unless the `Object` version just hands
>>>> off to the `Number` or `String` version based on the first operand type.)
>>>>
>>>> ## Semantics
>>>>
>>>> Using functions gives us the opportunity to use slightly different
>>>> semantics:
>>>>
>>>> 1. `true`: The operands are the same type and the relation is true
>>>> 2. `false`: The operands are the same type and the relation is false
>>>> 3. `undefined`: The operands are of different types
>>>>
>>>> This takes advantage of the fact `undefined` is falsy to not get in the
>>>> way of people just using the result in a condition, but if they examine the
>>>> result itself, it's possible to differentiate between #2 and #3.
>>>>
>>>> Sadly, `Object.is` (the exposed version of the SameValue algorithm)
>>>> does not make this distinction.
>>>>
>>>> # Non-symbolic operators
>>>>
>>>> JavaScript already has at least one binary operator that isn't
>>>> symbolic: `in`. Maybe there's a case for adding more. Brendan Eich is [on
>>>> record](https://esdiscuss.org/topic/suggestion-mapping-symbo
>>>> lic-infix-ops-to-binary-functions#content-5) five years ago as having
>>>> issues with them:
>>>>
>>>> > > modulo, div, divmod, has, extends
>>>>
>>>> > These are much better as methods. Polyfillable, but also not subject
>>>> to weird line terminator restrictions on the left. Same arguments killed
>>>> is/isnt.
>>>>
>>>> Hence `Object.is`, presumably (the linked discussion was about infix
>>>> functions, not `is`). I don't know if that view has shifted in the
>>>> subsequent five years; there have been big changes in the way JavaScript
>>>> moves forward. But that was an objection at least then.
>>>>
>>>> ## Form
>>>>
>>>> `lt`, `lte`, `gt`, and `gte`. And while we're at it, `eq` and `noteq`.
>>>> So:
>>>>
>>>> ```js
>>>> if (a lt b) {
>>>>     // ...
>>>> }
>>>> ```
>>>>
>>>> To avoid breaking the web, the new non-symbolic operators would have to
>>>> remain valid identifiers, only being operators by context, a bit like how
>>>> `for` can be a literal property name (`obj.for`) as of ES5 because we know
>>>> from context that it's not the `for` statement. But I assume (not being a
>>>> parser guy) that it's more complex to handle the above (those "weird line
>>>> terminator conditions on the left" Eich mentioned).
>>>>
>>>> ## Semantics
>>>>
>>>> Like functions, non-symbolic operators let us consider slightly
>>>> tweaking the semantics, e.g. that `undefined` result for operands of
>>>> different types I mentioned earlier.
>>>>
>>>> # Wrap-up thoughts
>>>>
>>>> Unless it's left to userland, the simplest, least cage-rattling
>>>> approach is to add functions to `Object`, but it's also ugly to use. It
>>>> does have the benefit (in my view) of letting us tweak the return value
>>>> when the types don't match.
>>>>
>>>> It seems to me the second least cage-rattling is to add symbolic
>>>> operators consistent with `===` and `!==`.
>>>>
>>>> -- T.J. Crowder
>>>>
>>>> On Wed, Apr 12, 2017 at 7:05 AM, Michael J. Ryan <tracker1 at gmail.com>
>>>> wrote:
>>>>
>>>> Thinking on it... (Number|Object|String) .strict(Equal|Greater|Less...)
>>>> Methods (a, b) might be better...  If either value isn't a match for the
>>>> bound type, it's a false, even if both sides are equal...
>>>>
>>>> Ex,.
>>>>
>>>> Number.strictEqual(null, null)  false
>>>>
>>>> Object.strictEqual(1, 1)  false
>>>> ...
>>>>
>>>> If you're doing a strict compare, one can presume you should know what
>>>> you're comparing.
>>>>
>>>>
>>>> --
>>>> Michael J. Ryan - tracker1 at gmail.com - http://tracker1.info
>>>>
>>>> Please excuse grammar errors and typos, as this message was sent from
>>>> my phone.
>>>>
>>>> On Apr 11, 2017 10:46 PM, "felix" <felix8a at gmail.com> wrote:
>>>>
>>>> Maybe every operator can have a non-coercing variant?
>>>>
>>>> One possible syntax is to have a modifier on operators
>>>>     x = a (<) b (+) c (&&) (!)d;
>>>>     if (x (!=) y) ...
>>>>
>>>> Another possible syntax is to have a modifier on expressions
>>>>     x = #(a < b + c && !d)
>>>>     if #(x != y) ...
>>>>
>>>> On Tue, Apr 11, 2017 at 7:48 PM, Darien Valentine <
>>>> valentinium at gmail.com> wrote:
>>>> > Although I’m unsure if this is wise given there are already eleven
>>>> symbols
>>>> > that are combinations of `=` and `<`/`>`, for symmetry with `==` and
>>>> `===`
>>>> > I’d imagine something like this:
>>>> >
>>>> > ```
>>>> > COERCIVE  STRICT
>>>> >>         =>=
>>>> > <         =<=
>>>> >>=        =>==
>>>> > <=        =<==
>>>> > ```
>>>> >
>>>> > Could also follow the pattern `>==` (strict GT) and `<===` (strict
>>>> GTE),
>>>> > which avoids the awkwardness of the latter two sharing opening chars
>>>> with
>>>> > `=>`, but that seems more ambiguous since `>==` doesn’t let you infer
>>>> > whether it means strict GT or strict GTE.
>>>> >
>>>> > It’d be nice to have this functionality built in, but I wonder if it’d
>>>> > possibly be preferable to provide it through methods of one of the
>>>> built-in
>>>> > objects, rather than as operators. Functions after all are more
>>>> flexible.
>>>> >
>>>> > _______________________________________________
>>>> > es-discuss mailing list
>>>> > es-discuss at mozilla.org
>>>> > https://mail.mozilla.org/listinfo/es-discuss
>>>> >
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>
>>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170412/1f1d64ea/attachment-0001.html>


More information about the es-discuss mailing list