typeof extensibility, building on my Value Objects slides from Thursday's TC39 meeting

Allen Wirfs-Brock allen at wirfs-brock.com
Fri Aug 2 15:02:18 PDT 2013

On Aug 2, 2013, at 12:59 PM, Tab Atkins Jr. wrote:

> On Fri, Aug 2, 2013 at 12:06 PM, Allen Wirfs-Brock
> ...
>> 'size' is essentially a primitive of the key/value store.
> It can be defined in terms of iterations, but I'm fine with this being
> primitive because it's such a small data leak.  If you're subclassing
> Map such that the naive .size isn't what you want, you're doing
> something rather unusual.

More importantly, if you actually have a reason to taking the size of a Map (perhaps to preallocate an ArrayBuffer to hold a copy of the values) then you probably would like to avoid iteration size performance hit to compute the size value.

>> ...
> I'm less convinced of 'clear' - it's clearly a product of iteration +
> 'delete'.  You're right that it's often more efficient to do directly,
> though.

Again it's pragmatics. If you have a reason for doing clears you probably want them to be fast.
> Yes.  You need exactly one of the iteration methods as a primitive.
> Today's world, where you have to override all four of them if you want
> to override any of them, feels very similar to forcing someone to
> override all six boolean comparisons rather than just defining them
> all in terms of less-than and equality.  (Like Brendan, I prefer the
> default to be one of the external iterators, either keys() or
> entries().)

Sure, the four methods are trivial and all the actual semantics are in the "MapIterator" class.  The only reason you have to over-ride four methods is that in the current design the reference to the "MapIterator" constructor is private and hard coded.  They way we would do this in Smalltalk is to have an over-ridable getMapIterator (we wouldn't actually say "get") accessor property on Map.prototype (don't have one of those either...) that return the MapIterator factory.  You'd only over-ride that one accessor (plus write a new iterator and factory).

I'd be fine with doing this approach.  I've generally, tried to minimize the number of OO hacks I've used in the spec. because not lot of people are familiar with them and others are uncomfortable with this style of extensibility.

>> That is exactly the interface of ES6 Map except that Map adds the trivial iterator factory method.  It simply isn't worth having the added complexity of distinct Map and Map-lite classes simply to not have the iterator factory methods. For ES6 Map is Map-lite
> Except for the iterators and maybe 'clear', yes.

and iterator can be fixed as above.

>> We can debate whether future new methods should be added to Map or be made part of  distinct new classes.  I'd suggest the latter for most cases.  New methods are likely to incorporate domain concepts (eg, DOM stuff) that are not an essential part of the basic key/value store abstraction.  It's better to define an appropriate domain specific abstraction that encapsulates a Map (or if you're an old school OO'er subclasses it) than it is to start adding new methods.
> I find it quite likely that we'll add more Map methods in the future.
> For example, .update() from Python's dict is easy to write yourself,
> but it's useful *often enough* that it's likely worthwhile to add.
> Same with .pop(), .popitem(), and setdefault().  It bothers me that if
> an author monkey-patches these onto Map.prototype themselves, they'll
> automatically work on my subclass (because they're defined in terms of
> the primitives that I override), but if we then add it to the
> language, it'll break (because it's defined in terms of [[MapData]],
> which I can't intercept access to).
> (Though, Brendan says in his response that we probably shouldn't, and
> should instead just pledge to only add new methods onto Map
> subclasses.  I don't think that's a particularly good idea, but it
> would address most of my problem.)

I agree that this is the preferred style of extensibility for both application developers and for libraries.  Creating an enhance/specialized subclass avoids unintentional usage and conflict.

>>>> The even simpler course is to keep Map as spec'ed and make DOM or other
>>>> specs work harder. Trade-offs...
>>> The reason I'm really pushing on this is that it's not just DOM (we've
>>> already made the trade-off there, by adding [MapClass] to WebIDL), but
>>> user-space as well.  I can't make a safe Counter class that's built on
>>> a Map unless I make the tradeoff above.
>> Did you mean [[MapData]]?  [[MapData]] is just private state of the built-in object.  If you're saving that you can't safely define certain abstractions with access to private state mechanism, then I agree you.  But trying to turn [[MapData]] into an object level extension mechanism isn't the solution to your general problem.
> No, I mean [MapClass].
> <http://dev.w3.org/2006/webapi/WebIDL/#MapClass>  It's a WebIDL
> extended attribute - the syntax has no relation to your use of [[foo]]
> in the ES spec.  It declares the interface to be a Map subclass,
> setting Map.prototype on its prototype chain and providing default
> implementations of several Map methods for you.  (This is especially
> helpful on the spec level, as you can then just define what the map
> entries are and then methods like get() are auto-defined for you.)

Other than the fact that WebIDL seems to continue to want to say "interface" when it really means "class", I don't have any problem with that. I suspect that in many cases it might be better  to encapsulate rather than subclass map but in situations where the primary concept is that of a specialized Map, this seems fine.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130802/8eb8bfe6/attachment.html>

More information about the es-discuss mailing list