Additional cascading Map/Set/WeakMap methods?
On Sun, Jan 13, 2013 at 4:44 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
At the last TC39 meeting, it was agreed tothat the set/add methods would return the collection that to which something is being added.
This supports code patterns like:
someMap.set(key1,value1).set(key2,value3);
In making this change to the spec. I noticed several other methods that could reasonably be used in this same pattern. For example:
someMap.clear().set(aKey, aValue);
someSet.delete(oldMember).add(newMember);
Are there any objections to making the clear and delete methods of Map/WeakMap/Set also return the collection, just like set/add?
None from me. If I was a TC39 member, I'd object to not adding them. ^_^
delete is supposed to return whether the item was in the collection to
delete or not, which otherwise would require using has to check for before
deleting. I don't know how useful the functionality is, but wanted to note
it since it'd be lost with this change. Chaining clear
is an easy
definitely though.
On Sun, Jan 13, 2013 at 5:14 PM, Brandon Benvie <brandon at brandonbenvie.com> wrote:
delete is supposed to return whether the item was in the collection to delete or not, which otherwise would require using has to check for before deleting. I don't know how useful the functionality is, but wanted to note it since it'd be lost with this change. Chaining
clear
is an easy definitely though.
Why would it require such? Are you expecting it to throw if you try to delete something that's not there? Or just wanting to use delete() as a destructive sampling method on the Map?
Personally, I've never used the result of collection.delete
for anything
and I'm not confident that the use case exists. I just wanted to note what
was being lost by changing the return value.
Another possibility is to let map.delete(key) return the mapped value (a bit like Array.prototype.pop()). But I’m not sure how useful that is, either.
Since then, due to issues pointed out here on es-discuss, I withdrew my consensus from this decision.
See also esdiscuss/2012-December/026971
where Andreas states the issue more clearly than I did.
My reversal was posted at esdiscuss/2012-December/026932.
Thanks there to Jussi Kalliokoski for also clearing up this issue.
In that case, it would be really nice for .set to return the value so these work:
return map.has(key) ? map.get(key) : map.set(key, val);
and
return map.get(key) || map.set(key, val);
On Jan 13, 2013, at 5:14 PM, Brandon Benvie wrote:
delete is supposed to return whether the item was in the collection to delete or not, which otherwise would require using has to check for before deleting. I don't know how useful the functionality is, but wanted to note it since it'd be lost with this change. Chaining
clear
is an easy definitely though.
Cascading delete and delete with existential result are clearly alternatives that can't both be accommodated in the collection API.
The existential result alternative is an optimization that saves doing two hash probes of a collection in certain circumstances. The attractive thing about it is that it is an optimization that you can't make in ES code if you only have has and and cascading delete. So, in a perf critical situation it provides value that a ES programmer could not obtain without it.
However a has/delete sequence can be fairly easily optimized at an implementation level. Just cache the last hash probe and check if the next probe is for the same key. But, unless it is a common perf bottleneck (or appears in an important benchmark) implementation are unlike to actually implement such an optimization.
So, we can design the optimization into the Map API by having Map delete return a Boolean or we can have a cascading Map delete and depend upon implementations to optimize successive lookups of the same key, if it proves important.
I don't yet have a strong preference between those two alternatives.
+1 since that was my initial (forever dreamed) proposed idea :-)
On Jan 13, 2013, at 6:47 PM, Mark S. Miller wrote:
My reversal was posted at esdiscuss/2012-December/026932. Thanks there to Jussi Kalliokoski for also clearing up this issue.
Well, I don't agree with Jussi's contention that it is an anti-parttern. It's a pattern, all right, and whether it a "good pattern" or a "bad pattern" is subjective.
There are reasonable arguments, on both-sides of this issue. Personally, I won't want to force anyone to either use or not use the pattern. Of course, no body is ever force to use a "cascading" result even if an API provides. However, if we excluded the cascade result at the API design level then we are making it impossible for those who like the cascade pattern from using it.
My Libertarian streak says, in reasonable situations give JS programmers the rope and let them decide whether and how to use it.
Allen the cascading thing is usually leading to think everything is cascading and there will always been cases where methods will break that cascading thing ...
In these case we have .has(key) able to break the "keep going and do stuff" with the instance.
In a direction where monocle mustaches has been applauded from any side and where cascading stuff ain't needed anymore since this will be not ambiguous and desired:
var another = map.{ delete(key); set(other, value) get(another); };
I don't see any valid reason to promote a jQuery style API for fresh new ES6 stuff while what suggested by me and agreed by many others in the other thread would be probably a better option against a void return.
return map.has(key) ? map.get(key) : map.set(key, val);
Above logic is way betters, in my opinion, than a generic, pointless in this case, 'this'
br
and that was ... U_U
var anotherValue = map.{ delete(key); set(other, value); get(another); };
On Sunday, January 13, 2013, Andrea Giammarchi wrote:
and that was ... U_U
var anotherValue = map.{ delete(key); set(other, value); get(another); };
The cascade operator was too late for ES6.
I still strongly agree with returning the collection post-mutation and I think Allen's position above is the most progressive so far.
The opposing argument has consistently given one example (the ternary) along with "I don't like it"—stop using this argument, it's neither compelling, nor does it represent the preferences of the JavaScript programmer community—a vocal minority at best.
On Jan 13, 2013, at 8:18 PM, Andrea Giammarchi wrote:
Allen the cascading thing is usually leading to think everything is cascading and there will always been cases where methods will break that cascading thing ...
In these case we have .has(key) able to break the "keep going and do stuff" with the instance.
In a direction where monocle mustaches has been applauded from any side and where cascading stuff ain't needed anymore since this will be not ambiguous and desired:
var another = map.{ delete(key); set(other, value) get(another); };
I don't see any valid reason to promote a jQuery style API for fresh new ES6 stuff while what suggested by me and agreed by many others in the other thread would be probably a better option against a void return.
New syntactic features and API design aren't alternatives or mutually exclusive. We may choose to avoid syntax design issues by not adding the syntax, but if we have an API, we have to do API design.
return map.has(key) ? map.get(key) : map.set(key, val);
Above logic is way betters, in my opinion, than a generic, pointless in this case, 'this'
This is another valid use case that is incompatible with chaining. Opinions will differ on which has broader utility. So, how do we choose. Deciding to make set/add return undefined is either a cop out on that decision or a principled stand against all such method chaining patterns. But we know that not everybody agree with that latter position.
Allen Wirfs-Brock wrote:
if we excluded the cascade result at the API design level then we are making it impossible for those who like the cascade pattern from using it.
The biggest thing I got out of the discussion Mark linked to is that the cascade pattern is potentially supportable with an operator in a future ES. It seems like if there's a pretty good possibility a cascade operator will be something we can look forward to in ES7, then it'd be redundant to have methods which return this
-- which frees up mutation methods to return other, potentially meaningful things (true/false for delete, value for set, etc).
Nathan
precisely and agreed 100%, this was my point again ... we know now that monocle mustache is good and we would like to change last minute some ES6 collection in a redundant way, accordingly with what we know already is going to be good for ES6 ?
As so, if returning this
cannot be accomplished otherwise, returning the
value cannot be accomplished as well so what is exactly that point about?
I believe the most common JS pattern out there is this one:
obp.prop = obj.prop || value;
Above pattern is a mistake already, specially in an Object.observe or new Proxy prospective, where getters, records, or setters, are invoked all the time compared with this pattern which is promoted by me constantly but ignored by the community
"prop" in obj || (obj.prop = value);
The community is not always aware about the possibility to have Object.observer or new Proxy but will follow the known pattern regardless tomorrow. That pattern is:
var valueINeed = "prop" in obj ? obj.prop : obj.prop = value;
so that they can use valueINeed
whenever they want passing through the
obj.property
JavaScript makes this easy to manipulate/use and hard to deeply explain in details (which is one of the wins of the language, by the way) so I think an equivalent:
var valueINedd = obj.get(key) || obj.set(key, value)
is helping much more the community than an implicit returning get.
Said that, we should also consider that whoever would like to implement a
library that returns this
rather than the value will do in any case, same
way I have implemented what I am talking about in my HybridMap proposal (in
github and I am not going to put the link here, is just ES6-collections
with returning value and a meaningful delete(key):boolean )
br
I have a couple of questions:
-
Isn't chaining now considered less-good style compared to a newer jQuery API that takes an object literal full of properties to add?
-
Is there any precedent for delete-chaining in any library?
For what I can tell
-
when obj.{...} will be available in coffeeScript and transpilers nobody will ever care about the "return this" 2004 style like API (long glory to this API)
-
never seen delete used chained but I've seen .remove(item) used chained, if that's a valid metaphor/equivalent
in DOM world removeChild returns the child, so you can chain, but in jQuery remove(child) returns the parent and you keep chaining
Moreover, and now that I think about it, delete/clear chained are controversial because while I agree delete should return a boolean, simply because I am used to delete operator, clear might return the same accordingly.
I mean, delete returning false means that a property could not have been removed from some reason so clear should take care of that property that cannot be removed, right?
In ES5 world would be something like:
var o = Object.create({ clear: function () { for (var k in this) delete this[k] ; return this; }, "delete": function(k) { return delete this[k]; } }, { deletable: { enumerable: true, configurable: true, value: "deletable" }, stillthere: { enumerable: true, value: "stillthere" } });
alert([ o.delete("deletable"), //true o.delete("stillthere"), //false o.clear().stillthere //stillthere // stillthere" ]);
So while I've always been convinced about clear():this ... I start thinking for consistency with delete it should not return the instance ^_^ ... let's drop the returning instance everywhere ? :D
At the last TC39 meeting, it was agreed tothat the set/add methods would return the collection that to which something is being added.
This supports code patterns like:
someMap.set(key1,value1).set(key2,value3);
In making this change to the spec. I noticed several other methods that could reasonably be used in this same pattern. For example:
someMap.clear().set(aKey, aValue);
someSet.delete(oldMember).add(newMember);
Are there any objections to making the clear and delete methods of Map/WeakMap/Set also return the collection, just like set/add?