4 ms·
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 wit
by leosanchez 3y ago
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).
- wk_end 3y agoIt works fine on objects. 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. [0] https://github.com/tc39/proposal-record-tuple https://github.com/tc39/proposal-record-tuple
- leosanchez 3y ago> I'm guessing you're taking issue with the fact that it uses reference equality instead of structural equality That and other programming languages provide a mechanism to specify a property to check for value equality.
- solardev 3y ago(Not directed at you, but the JS implementation) 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 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. It's so frustrating to watch.
- Gare 3y agoThat'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".
- solardev 3y agoWould 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.
- ameliaquining 3y agoMy 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.
- TurningCanadian 3y agoIt 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.
- seniorsassycat 3y agoInstead, or in addition to records and tuples I want Symbol.equals or some other interface for equality.
- The_Colonel 3y agoYou'd also need Symbol.hashCode, since you wouldn't be able to implement a fast lookup just with equals method.
- spion 3y agoWhile 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?
- spion 3y agorelevant issue, which is at the crux of this problem: https://github.com/tc39/proposal-record-tuple/issues/387 https://github.com/tc39/proposal-record-tuple/issues/387 and which shows that a symbol based protocol was the kind of approach that could've worked from the start. 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.
- tshaddox 3y agoWhat do you mean by "why won't it work on objects"? Objects work fine as Set members. const a = {} const b = {} const set = new Set([a]) set.has(a) // true set.has(b) // false
- leosanchez 3y agoAFAIK there is no mechanism to do the following const a = { value: 1 } const b = { value: 1 } const set = new Set([a, b]) set.size will be 2 as JavaScript checks for reference equality and has no mechanism check equality on a specific property
- lolinder 3y agoIt 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.
- leosanchez 3y ago> The actual problem here is that JavaScript doesn't have any way to override `equals` and `hashCode` like Java does But you can use Symbols for that. no ?
- lolinder 3y agoTheoretically, if they were to add the ability to override equals they'd do it through symbols, but they haven't yet: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#static_properties https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- leosanchez 3y agoYes. Symbols are the obvious ones. Symbols and sets both came with ES6? I believe. I wonder why they haven't implemented something like that.
- vlovich123 3y agoCouldn’t you polyfill your own Set implementation using Map<Symbol, Object> where the symbol is constructed how you need it from the object?
- samtho 3y agoThe 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?
- leosanchez 3y ago> 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.
- samtho 3y agoThat’s fair, I would actually love to have a ‘Symbol.compare’ to implement so instances can be sorted and checked for equality.
- Gare 3y ago> even python you can do that with __eq__ i think In Python __hash__() needs to be implemented in order to put something in a set. You cannot override that in Javascript, but you can roll your own set with custom hash function by wrapping a Map.
- leosanchez 3y ago> You cannot override that in Javascript True. But you they can use a "known Symbol" to implement this feature.
- Gare 3y agoImplementing this on mutable objects is a dangerous game. That would be quite the footgun (and JS has plenty of them already). IMHO it's better to be explicit in such a case. It's not complicated to implement your own set-like class that does exactly what you want.
- leosanchez 3y ago> That would be quite the footgun Could you explain why that would be a footgun? JavaScript already has known Symbols like hasInstance which is kind of similar to this.
- Gare 3y agoIt'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.
- ivanjermakov 3y agoI 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
- TheRealPomax 3y agoThat's not specifically related to Set, though? That's "how the language works"?
- jupp0r 3y agoThis 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.
- joe_guy 3y agoAn annoyance I have is "add" does not return a boolean indicating if the set already contains the entry. Most runtimes do this to prevent the need for a double lookup cost by doing "has" then "add." It's a common pattern for when the set is used to either know when to or when not to do expensive work.