Agreed. And the reason why the default array sort behavior is good is because it avoids one of the places where that shows up.
The alternatives were:
- don't have a default method at all (arguably the correct choice)
- fundamentally change how Javascript works, breaking the web in the process
- turn the default sort method into an arcane, unpredictable monster
By comparison, "turn everything into a string" is pretty reasonable behavior. Given what Javascript is, string conversion isn't a gaffe or thoughtless design decision, it is the correct behavior for a default Javascript sort function to have.
You can still do the cast if needed when they're different types (once the < operator is applied). Whatever, that's the norm for JavaScript. But in this case, the cast to a string happens before it even knows it needs to, which is what makes the OP's case so egregious. In this case all the values are the same type - numbers - and they still get needlessly cast to strings. IMO, waiting until the < to do any casting is not only more reasonable overall, but is actually more inline with expected JS behaviors.
Under the current algorithm,
randomize_array(['3', 5, 10, '11']).sort()
will always give you [10, '11', '3', 5]
Under your proposal, where our casts are dependent on what two elements we're currently comparing, you might get [10, '11', '3', 5]
or ['3', 5, 10, '11']
Try it yourself: ['11', 10, '3', 5].sort((a, b) => a < b ? -1 : 1);
[10, 5, '3', '11'].sort((a, b) => a < b ? -1 : 1);
The only way that type-specific sorting algorithms work is if we check the entire array before we start sorting, which has performance implications. It can't be on-the-fly, we either have to commit to type-checking the entire array, or commit to casting the entire array. I think casting is more consistent, I don't like the idea of an algorithm changing underneath me because of a type error. But in either case we can't change our sorting behavior mid-sort.This is part of the reason why recommendations on how to sort an array of integers is to use a sort method like
(a, b) => a - b
instead of the comparison operator -- because most of the time subtraction will cast everything to a number across the entire array, including the strings. For most people, this will be fine. To head off what would logically be the next thing to suggest, we also can't rely on `a - b` for a default sorting algorithm for `sort2`, because of ['11', 10, '3', 5, {}].sort((a, b) => a - b);
[{}, 10, 5, '3', '11'].sort((a, b) => a - b);
> If your assertion is that all of those should just throw errors and we should stop doing type inference entirely, then that's kind of reasonable, I guess
The thing is that the above isn't actually type inference, it's type casting. There's a difference. And in JS, implicit casts happen not just in "reasonable" cases like int -> float, but between every possible type:
This, to my mind, is JavaScript's greatest remaining flaw in 2020. It's the whole reason the triple-equals (===) operator was added, but that only solves one common case.So my basic argument is: in totally unreasonable implicit-casting scenarios (which is most of them), it's much better for a language to err than to make a "best effort" guess at casting the values to be the same type.
All of that said: JavaScript is the way it is, and unlike lots of other things that have been papered-over with new features over the years, this issue is so foundational that I don't think it can ever be fixed outside of just learning to avoid the places where it shows up.