Having read the article... it doesn't? It gives a nicely elaborate overview of the original Set and the new functions, in a way that lets folks who've never use sets (except to hide "filter for uniqueness" functionality) know that it might actually be useful to them.
Javascript Set is the least useful Set there is across languages I have used.
Why won't it work on objects when the language very good for creating objects with any ceremony.
EDIT:
By won't work i mean you are left with reference equality checks which I never felt useful.
Talking about other languages i worked with, In Java and C# you can override equals method in which you specify which property to check equality on, (even python you can do that with __eq__ i think).
const set = new Set();
const x = { id: 'some object' };
foo.add(x);
foo.has(x);
> true
I'm guessing you're taking issue with the fact that it uses reference equality instead of structural equality, which can definitely be a pain point. There's a proposal to improve the situation with "Records and Tuples" [0], but it's been stuck in committee hell for years.
I don't understand the point of this. What good is an object in a Set if it can't be deduped?
With this and so much else in the core language, I wish they just incorporated lodash into Ecmascript and called it a day. https://lodash.com/docs/4.17.15#uniqWith (the example is specifically for comparing objects by value)
> but it's been stuck in committee hell for years.
It's been like this for more than a decade, too. I've never seen a popular language evolve SO slowly, whether it's with Sets, or things like the Temporal API or TypeScript or JSX or bundlers. Almost all the innovation in JS seems to come from third parties forced to hack them in, whether through vendor prefixes, jQuery, or later ecosystems like npm libs and React. In that same time period, PHP improved by leaps and bounds, entire languages like Rust came out of nowhere and gained a foothold, WASM was developed, etc. And lodash is STILL around and still useful.
JS sets are plenty useful. Sometimes you want reference equality for objects (though letting programmers choose would be ideal).
As far as the pace, I think they do it about right. JS has hard requirements to be backwards compatible and has several canonical implementations, so adding new features should only be done after careful consideration. Letting users add features themselves and ensuring they have staying power and wide adoption is a good test.
That's the curse of a language that has to be supported by several competing implementations, and therefore all have to agree on any additions. Also, it's important not to "break the web".
Would it really break the web to have a basic, standard implementation of "compare object by value"? Or a first-party type system?
I think the inverse can also be true, where the core language / TC39 is over-cautious with feature adoption, leading to extreme ecosystem fragmentation and eighteen vendors reinventing the same three wheels every year.
My understanding of the problem with records-and-tuples (which is what "objects comparable by value" would look like) that's kept it stalled out is that it breaks all the engines' internal assumptions about how things work, so they'd have to do a large amount of refactoring, and they'd have to do it in a way that doesn't regress performance for code that doesn't use the new features. Given how complicated modern JS engines are, this is just a lot of work and a big ask. I do hope that it happens eventually.
As for a first-party type system, would you want it to basically be TypeScript, or something else? If the former, then we'd lose a lot of features and such, because the core language, not being a single vendor, can't move as fast as TypeScript does. If the latter, then it would be necessary to define what, and in all likelihood different people would want different and incompatible things.
It seems like they're worried that interning of objects/records would be too expensive to do generally. It's hard to predict though: that overhead would only apply to new code using R&T and has to be weighed against the elimination of recursion for deep comparisons and freezing, fewer re-renderings when value-equal but not reference-equal objects are encountered, the greater possibility of memory reuse across deep clones, and other performance optimizations that would be unlocked by true immutability.
Aside from performance, true native immutability would bring huge improvements to how JS programmers can reason about their code. Not having to worry about mutation makes a whole class of possible bugs disappear. Having to rely on third party libraries (or deep freezing manually) for immutability is really holding back the language.
I think "basic implementation of compare by object" is actually not that simple. Either you need to be able to provide an equality function and hashcode function (for efficiency) or some generic comparison which is useful only for a subset of cases, potentially very slow and brittle.
It's probably about the right speed for a language with an implementation as complicated as this that's unfortunately stumbled backwards into enabling all web page dynamic behavior on the planet.
JavaScript is a secure language and therefore doesn't have the luxury of a language like C++ where you can mash two features in and declare their interaction "undefined behavior." Every new feature must eventually be considered in the context of every other feature.
While that could help, I don't see why records and tuples are necessary. We added symbols precisely to deal with this issue in a backward compatible way. We could add symbols for set/map protocols using `equals` and `getHashCode` which would enable any objects to get set/map deduplication functionality. Those implementation could then be the mechanism how its implemented for records and tuples.
If this is a bad way to do it, then why isn't TC39 working on a better way to implement protocols / traits in JS?
See `Symbol.keyEquality` - although that still shows what I feel is a misunderstanding about the direction at which the protocol should work. I don't want to be creating new types of maps and sets, I want existing ones to have controllable key equality. If it was for new types of maps and sets, I'd just implement a new Set class independent of JS builtins and be done with it. (Its not like the built in Set offers any rich features that I'd have a hard time replicating anyway)
Protocols (traits) should really be the cornerstone of TC39 work, IMO. They'll help with JavaScript's serious ecosystem compatibility issues.
It does work for objects, but I imagine the problem you're describing is that JavaScript objects are equal if and only if they're the same reference, so you get this:
const set = new Set();
set.add({});
set.add({});
// Set has 2 items [{}, {}]
const a = {};
set.add(a);
set.add(a);
// Set has only 3 items [{}, {}, {}]
The actual problem here is that JavaScript doesn't have any way to override `equals` and `hashCode` like Java does, so there's no way to change this reference-equality behavior.
This has nothing to do with the language and is just a huge flaw in the design of the standard library: inability to supply a comparator function that is used for data structure types like sets, etc.
I think this is the feature I miss the most in TS: ability to implement equals, compare and hash for my types and have it working with standard Map and Set
The language is good at creating literal objects of no particular type/class/prototype. The concept of a Set is closer to an Array of unique Numbers than one of literal objects:
const s = new Set([1,2,3]);
The resulting instance of Set can be queried for something existing in the set, have items added to the set, and remove from the set. This doesn’t handle literal objects (e.g. a record) well, which isn’t idiosyncratic to JS as most languages treat objects as reference-ish. The solution be to use a literal String or Number identifier that can be looked up.
I’m genuinely curious, what features from other languages do you want to see in this implementation?
> I’m genuinely curious, what features from other languages do you want to see in this implementation?
In Java and C# you can override equals method in which you specify which property to check equality on, (even python you can do that with __eq__ i think). In JavaScript you are left with either primitives or objects with references equality checks.
It's very important to ensure that the value of hash for a given entity doesn't change if it is to be relied upon, otherwise things like Map or Set will not function properly. Therefore, one should be very careful in implementing it yourself and ideally one should use immutable hashable structures provided by the language/stdlib (like tuples or sets in Python). Until Javascript has such structures which should be enough for majority of use cases (there is record and tuple language proposal, but it's in limbo) I think it's unwise to provide a way to sloppily emulate it in user code.
Yes, primarily driven by people that seem to have only ever used the JS set and are either confused as to why people are expecting more or surprised to learn what devs in other languages have taken for granted for decades.
The way to do it in EcmaScript-6+ is to create your own class MySet which extends the built-in Set. In it you then add or override any methods you want.
You cannot override the equality -operator but you can add simple short-named method "eq(anotherSet)" and perhaps variations of that.
It's not too much effort to create your own custom Set -subclass because it only needs to do what you need it to do that the built-in Set does not do. You can also perhaps find such an implementation on npm etc.
A perfect built-in Set would be great but being able to subclass the existing Set-class helps a lot already.
The number of existing actual set operations available on std Set is so low that that’s basically the same thing as creating your own custom Set over the std Set via plain, old composition.
But my point is when you code an application you are not coding a library, but an app, so you only need to add the methods your app needs. You probably don't need to create an all-encompassing new Set-implementation that wins the library-of-the-year award. :-)
Maybe you just need to add a method named 'eq()'.
An added benefit of creating your own Set-subclass is you can put debugger-statements in its code to observe who uses it and how. You can put assertions in it to ensure only correct type of elements can be added to it etc.
I was looking recently and found there doesn't seem to be any way to do lexicographic ordering of lists in JavaScript, unlike (almost) every other language I've ever used, where lists are ordered lexicographically by default.
Is there any magic quick way of doing this in modern JavaScript, or do I have to keep cutting+pasting a comparator I wrote everywhere?
Sorry, I mean the things I'm sorting are themselves lists. Imagine for example:
[ [1,23], [11,1], [11,44], [22,4] ]
I'd consider this list "sorted", as the inner lists are sorted lexicographically. Except, if I gave this list to Javascript and asked it to sort it, it would cast each of the inner lists to strings, which (for example) leads to [11,11] < [1,1], as "[11,11]" < "[1,1]"
A while back I wrote some RxJS operators to do these operations around the primitives (https://rxjs-ninja.tane.dev/modules/array.html) but glad to see they are finally adding them to the standard library.
Of all the HN rules (written and informal) I find the general air of
humourlessness most difficult to vibe with. I do understand the
reasons. We'd like to keep discussion focused and jokes sometimes
derail a sincere thread. Also, humour can be misinterpreted easily and
cause offence. But that comes in a context where sarcasm, dooming and
flippancy are rife and acceptable, and sometimes it really does feel
like someone just needs to lighten the mood a little. There's also
that humour can be a wonderful seed to pose a question or provocative
challenge that kicks-off a thread of intellectual curiosity and would
not work otherwise. Anyway I am glad that this is neither Slashdot nor
Reddit but has it's own atmosphere.
You’ve captured my disappointment perfectly FWIW, so triple thanks (for the laugh, the vocabulary of “humor punishment”, and this articulation). I'm also glad this isn’t Slashdot or Reddit, but sometimes it’s nice to get a reminder that it can also be pleasant here in its own right.
C++ 'sets' are ordered mutable binary search trees in most implementations.
Not the ties and hashtables of other languages.
With strict weak ordering, you really only have the ordering of to elements.
Ada took care of that polysemy between ordered and hash sets a long time ago.
But ya it took them to long to implement 'contains', but JavaScript requiring ordered sets while allowing multiple implementation details complicated it IMHO. Had they chosen a structure vs a time complexity it would have been easier to extend.
I know that, but that's a rather elaborate way of doing the main thing sets do, and you have to repeat the name of the set twice. Fortunately C++20 added set.contains(x). Of course set.count(x) also already existed but some would prefer set.find(x) != set.end() anyway for <reasons>, and it's the fault of the language's design to cause such opinions in the first place imho.