One common JS problem is NaNs ending up in unexpected places, and it's often difficult to trace back where they came from. Getting a type error instead of a NaN would be nice.
I think this is a reasonable argument for being able to write expressions with non-coercing operators, and this is why I'd lean toward annotating an entire expression as non-coercing, instead of doubling the number of operators. On Wed, Apr 12, 2017 at 9:25 AM, T.J. Crowder <[email protected]> wrote: > FWIW, I think the next steps for this discussion are: > > 1. To hear from people whether they feel the need for these operations in > their everyday work. It's interesting, you so often hear people saying > "Always use `===`, not `==`!" with...fervor...but apparently strict versions > of the relational operators aren't (or weren't) on people's minds. :-) > > 2. To hear from implementers about the difficulty level of adding four more > symbolic operators (`<=<`, `<==`, `>=>`, and `>==` or whatever they end up > being). > > (I like my non-symbolic operators -- `lt`, `lte`, and such -- but I doubt > they'd pass muster, for the reasons Brendan flagged up in the thread about > infix functions.) > > If the answer to #1 is "meh," discussions of operators vs. functions is > moot; nothing's going to happen. If the answer to #1 is "Oh yes, this would > be really useful" and the answer to #2 is "Fairly straightforward", that's a > solid steer as well, as is a "Actually, surprisingly hard" to #2 (and it > would be surprising, at least to me, but what do I know about implementing a > JavaScript engine). > > -- T.J. Crowder > > On Wed, Apr 12, 2017 at 4:49 PM, Michael J. Ryan <[email protected]> wrote: >> >> 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 - [email protected] - 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" <[email protected]> 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 >>> <[email protected]> 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 <[email protected]> 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 >>>>> <[email protected]> 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 >>>>>> <[email protected]> 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-symbolic-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 <[email protected]> >>>>>>> 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 - [email protected] - 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" <[email protected]> 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 >>>>>>>>> <[email protected]> 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 >>>>>>>>> > [email protected] >>>>>>>>> > https://mail.mozilla.org/listinfo/es-discuss >>>>>>>>> > >>>>>>>>> _______________________________________________ >>>>>>>>> es-discuss mailing list >>>>>>>>> [email protected] >>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>>> >>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> es-discuss mailing list >>>>>>>> [email protected] >>>>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>>>>> >>>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> es-discuss mailing list >>>>>> [email protected] >>>>>> https://mail.mozilla.org/listinfo/es-discuss >>>> >>>> >>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> [email protected] >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> >> _______________________________________________ >> es-discuss mailing list >> [email protected] >> https://mail.mozilla.org/listinfo/es-discuss >> > > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

