9 ms·
Not to defend JavaScript too much, but I think the three examples seem reasonable when viewed in the right way, and I think it's hard for JavaScript to do much
by alangpierce 9y ago
Not to defend JavaScript too much, but I think the three examples seem reasonable when viewed in the right way, and I think it's hard for JavaScript to do much better with its existing design philosophy (dynamic typing and coercion rather than runtime errors to deal with type mismatches).
>= and > are numerical operations, so both sides are interpreted as a number, and null is 0 when treated as a number. That means `null > 0` becomes `0 > 0`, which is false, and `null >= 0` becomes `0 >= 0`, which is true. In contrast, == is a much more general operation that works on numbers as well as objects, strings, and all sorts of other things, so it certainly doesn't make sense to always convert both sides to a number first. I'm certainly happy that `null == 0` is false in JavaScript, and I wouldn't want that changed to be consistent with numerical comparison (which comes up much more rarely than equality checks, at least in my code).
This type of issue isn't unique to JavaScript. In Java, if you have boxed Integers, `==` will do object identity comparison (so two different instances of the same number will compare as not equal), while `>=` etc will unbox them and do numerical comparison, which I think stems from the same issue of equality being more general than numerical comparison.
Also worth noting that it's not always true that `a >= b` means `!(a < b)` in JavaScript. `'a' >= null` and `'a' < null` are both false.
- deleted 9y ago[deleted]
- nine_k 9y agoAggressive coercion in an attempt to execute clearly faulty code is exactly the problem.
- microcolonel 9y agoBut not all code that looks faulty is faulty. I know plenty of competent people who have used the numeric properties of undefined and null directly with numeric operators knowing exactly what they're doing. Likewise there is a natural association between nonzero and true, zero and false, which is very useful (multiplying by a boolean, accumulating a list of booleans in a collection of records to get a count). If you're using JavaScript, I encourage you to embrace the dynamic, coercive funky nature of the language. There is a twisted logic to each twisted feature.
- deburo 9y agoBut the code using twisted features is hard to maintain afterward. You will have to put comments around them just to be sure the next guy (it might even be the author himself) doesn't waste its time understanding what the hell the code is doing.
- microcolonel 9y ago> But the code using twisted features is hard to maintain afterward. let a = 0; for (const record of records) a += record.bool; What's so bad about this? (aside from mutable accumulator, but that can be restricted to one scope). The pattern is basically directly lifted from C, where there's thankfully no such thing as a boolean type. If anything, the solution is to fold Boolean and null into Number to begin with. > You will have to put comments around them just to be sure the next guy (it might even be the author himself) doesn't waste its time understanding what the hell the code is doing. I sincerely hope that anyone who writes JavaScript on my team knows the coercion rules in JavaScript; otherwise they should probably be writing some transpiled language that lacks these features.
- YSFEJ4SWJUVU6 9y agoNon-zero does not imply `1'. >The pattern is basically directly lifted from C, where there's thankfully no such thing as a boolean type. There has been a boolean type in the current standard for C for longer than some of the younger people here have been alive.
- microcolonel 9y agoWow, I would never have known about stdbool. I have never seen it used (explicitly), even though it's been around since I was two years old. Thanks for bringing this to light! :- ) Nonetheless, bool or _Bool (aside from having implicit saturating arithmetic and assignment) is a one-bit integer type. It seems it mostly just formalizes the (implied, fictional) narrowing conversion involved in testing conditions, which is probably why I've not seen it used explicitly. The macros true and false seem to expand to 1 and 0 respectively.
- irascible 9y agoBe liberal in what you expect and precise in what you emit.
- mistercow 9y agoThe most common mistake I see when people talk about JS quirks is to assume that this kind of feature is always about fault tolerance rather than convenience. For example, many languages treat conditions as "if nonzero". From a certain perspective, you could argue that if (i--) { is "clearly faulty code". From another perspective, you would say it's a shorthand whose meaning is obvious to anyone familiar with the rules of the language. In my view, JavaScript's sin is that its rules are complicated, and it's difficult to statically analyze, so tools intended to guard against its pitfalls can only do so much. Whether or not the complicated rules are about faulty code is pretty irrelevant. ASI is an error correction mechanism, but its pitfalls are easy to detect, so in modern environments it doesn't matter. The behavior of comparison operators seems to me like more of a convenience feature, but it's still a problem; the rules are easy to forget, and only a very draconian linter rule can protect you.
- marcosdumay 9y agoHonestly, I like implicit coercion of empty stuff into false. Yes, I do agree it makes the code more legible. That said, objectively it doesn't gain that much. It may be that the cost of having any kind of implicit coercion is bigger than such gain. Implicit numeric coercion is another topic to think about, it is also hard to be sure if it is a net gain.
- justinpombrio 9y agoLet me agree with you but then turn that on its head. You've argued that having `null >= 0` is the best that JS could do while having a coercion-based semantics. And I agree: this confusing behavior may be the best you can possibly do with a coercion-based semantics. And it's not just this example, either. JavaScript is filled with gotchas, and a lot of them have to do with automatic coercion. For example, did you know there are number and string values x, y, and z, such that x<y, y<z, and z<x? But if this is really the best you can do in the presence of automatic coercion, maybe that's an argument against coercion.
- lisper 9y ago> having `null >= 0` is the best that JS could do while having a coercion-based semantics. And I agree Well, I disagree. You have to add a third condition for this to be the best you can do, and that is that the boolean type has to be two-valued. But that is not a given. Just as numerical types can include infinities and NaNs, a boolean type could include a third value that is neither true nor false. The IF statement would have a third clause to handle this value, something like: IF condition THEN [code if condition is true] ELSE [code if condition is false] OTHERWISE [code if condition is neither true nor false] Like ELSE, the OTHERWISE clause would be optional so this would be a backward-compatible change. By default, existing code that encountered non-true-non-false conditions would do nothing.
- fenomas 9y agoAre you seriously suggesting that, widgety though JS's implicit type conversions might be, if its booleans had three values then things would be better?
- jmfayard 9y agoBooleans having only three values would be an improvement for JavaScript where they actually have four : true, false, null and undefined
- 9y ago
- YSFEJ4SWJUVU6 9y ago>Also worth noting that it's not always true that `a >= b` means `!(a < b)` in JavaScript. `'a' >= null` and `'a' < null` are both false. That also means that the concluding section of the article is simply wrong. To be honest, it's a pretty poor read – a waste of many words to explain a spec that's pretty straightforward itself, and ending up in a major misunderstanding.
- __s 9y agoYet 1 == "1". & comparisons also apply to string/string not through numerical conversion
- twiss 9y ago> Also worth noting that it's not always true that `a >= b` means `!(a < b)` in JavaScript. `'a' >= null` and `'a' < null` are both false. For anyone wondering why (like me), it's because +'a' returns NaN, and comparisons with NaN always return false (NaN <= 0, NaN >= 0 and NaN === NaN are all false). Specifically, the Abstract Relational Comparison algorithm for 'a' < null returns undefined (instead of true or false), and all comparison operators return false when it does that. [1] When neither argument is NaN, what the author wrote is true and a >= b === !(a < b). [1]: http://www.ecma-international.org/ecma-262/8.0/index.html#sec-abstract-relational-comparison http://www.ecma-international.org/ecma-262/8.0/index.html#se...
- colllectorof 9y ago>I think the three examples seem reasonable when viewed in the right way These kinds of statements is why I think webdev is completely and thoroughly fucked. When obviously bad software or language design is widely accepted on the basis of convoluted technicalities you know that thing will not get better, only worse. >This type of issue isn't unique to JavaScript. In Java, if you have boxed Integers Boxed types in Java were a clusterfuck of their own. They shouldn't be used as justification of bad design in other languages. A cautionary tale, more like.
- alangpierce 9y agoI think most people (including me) would agree that type coercion makes JavaScript a worse language, and that boxed types are one of the ugliest things in Java. By "the three examples seem reasonable when viewed in the right way", I mean that it's possible to build an intuition about how JavaScript behaves in these situations, and it doesn't require memorizing a giant spec. The behavior isn't random, it's the consequence of some early design decisions that likely should have been made differently, and understanding those design decisions will make you a better JavaScript programmer. Many smart people have been working on improving JavaScript for a while now, and many of the problems have been fixed either by language improvements or widely-available tools. The runtime behavior of basic operators can't be changed, though, so we'll likely be stuck with the type coercion rules for a long time, although it's worth noting that both TypeScript and Flow disallow `null >= 0`.