On Aug 1, 2013, at 5:36 PM, Tab Atkins Jr. wrote:

> On Thu, Aug 1, 2013 at 5:29 PM, Brendan Eich <[email protected]> wrote:
>> Tab is still looking for a MapLite (tm) that can be customized with hooks.
>> Obviously to avoid infinite regress, the MapLite bottoms out as native, and
>> the hooks are on the outside (in Map). Tab, do I have this right?
> 
> Right.  Map has a medium-sized interface, with several methods that
> don't provide new capabiltiies, but only convenience/performance.  As
> well, I expect the interface to grow over time.
> 
> Making a Map subclass that needs to audit its data on input or output
> is thus difficult, because when a new method gets added to Map, either
> it's able to pierce through your interventions and get at the raw data
> directly, or it simple doesn't work until you update.  Even for the
> existing methods, you have to manually override the conveniences; just
> overriding delete() just protect you against a clear().
> 
> MapLite has the smallest possible useful semantics for a Map
> (basically, the same semantics that the spec uses in terms of
> meta-operations), and thus is useful as an interception target.  You
> can then just subclass Map and only override the MapLite at its core,
> secure in the knowledge that new methods will Just Work (tm) in terms
> of the provided bedrock operations on the MapLite.

I'm all for extensible abstractions based upon abstract algorithms that 
decompose into a set of over-ridable primitives.  That that is pretty much what 
the current ES6 specification of Map provides.  I assume that we agree that at 
the core of map there needs to be an encapsulated implementation of a key/value 
store.  The question Tab is asking essentially is whether the methods of Map 
are too tightly couple to one specific key/value store implementation.  Or 
viewed from another perspective, would ES6 be more flexible if the public 
abstraction of Map and the default key/value store (Map-lite) were separated 
into distinct classes.  

Let's look at the Map methods to see if it is possible and worthwhile to make 
it anymore extensible then it already is and whether it makes sense to move 
some of the functionality into a Map-lite.

'get'/'set'/'delete': The fundamental operations of Map are 'get', 'set', and 
'delete'.  If you look at the specification of these methods, you will see that 
they are almost pure primitives dealing with the mechanism of the encapsulated 
key/value store.  If we separated the default key/value store into a separate 
Map-lite object it would have the equivalent of these methods and 
'get'/'set''/delete' on the  more generic Map would simply delegate to them.  
There is very little in the way of policy that could be separated from the 
default implementation. The major policy decisions within 'get'/'set'/'delete' 
are 'set' handling of duplicate elements, and 'get'/'delete' handling of 
missing elements.  Map's 'set' over-writes the value of an already existing 
element, 'get' returns undefined as the value for missing keys, and 'delete' 
returns false for a missing key.  A Map-lite store would still have to have 
some sort of defaults for these cases and these are good defaults for JS. I
 f you want to change those defaults it is easy to encapsulate or subclass a 
Map and over-ride these policies with get/set/delete wrappers.

''has': is also an almost pure operation upon the primitive key/value store.  
It would be possible for a Map-lite to expose a primitive method that combines 
the capabilities of 'get'/'has' such that Map might define its distinct 'get' 
and 'has'  policies in terms of that combined operation.  However the combined 
operation would require  more complex arguments and/or result design and 
additional logic in the Map-level methods.  I think it is more pragmatic to use 
a default primitive key/value store that directly exposes the default policies 
and push the complexity of  alternative policies into any new abstractions that 
wants them.

'size' is essentially a primitive of the key/value store.

'clear' could be expressed in terms of key iteration and the 'delete' methods. 
We haven't talked about iteration yet and whether it needs to be part of a 
basic key/value store API.  But we can skip ahead because 'clear' is really 
about pragmatics.  It's a method that is useful when a client knows it wants to 
delete all elements from a key/value store.  It primarily exists as a channel 
to convey that intent to lower level implementation layers, based upon the 
assumption that a bulk delete can probably be performed more efficiently than a 
bunch of individual deletes.  So, even if you have a basic key/value store you 
will want to have a 'clear' method on it to support such bulk delete use cases.

So ES6 Map with 'get'/'set'/'delete'/'has'/'size'/'clear' is essentially a 
"Map-lite".  What about the rest of Map's methods?

They are all about iteration. There is the 'forEach' method that directly 
iterates over the keys/values of a Map and 'keys'/'values'/'entries'/@@iterator 
all of which are used to obtain an iterator over the contents of a map. 

So should iteration be part of a Map-lite and should an iteration ordering be 
mandated. Regard ordering, JS experience shows us that developers generally 
expect iteration order to be consistent across all implementation.  So a 
Map-lite that supports iteration without a specified order would be an 
interoperability hazard. If Map-lite had no iteration methods we could still 
implement forEach and the iterator factories if Map maintain a separate 
ordering data structure that it updated on every 'set'/'delete' operation.  But 
such a side table is likely to be far less efficient than any means of element 
ordering that are directly implemented as part of the key/value store. So, so 
for ES, it makes sense for something like 'forEach' to be part of a Map-lite.  

The iterator factory methods of map are all just short-cuts for creating 
"MapIterator" objects which are current specified as "friends" (in the C++ 
sense) that know about internal key/value store encapsulated by Map objects.  
The current spec. for MapInterator was written before we added 'forEach' to Map 
and arguably it could be re-specified in terms of 'foreach'.  However, that may 
take away some implementation level flexibility.  Regardless, it's a 
possibility that I think I'll look into more deeply.

Where we end up with is that the operations we would want for a Map-lite are 
get/set/delete/has/size/clear/forEach. They all need to know the implementation 
details of the underlying key/value store and for pragmatic reasons it doesn't 
make sense to try to reduce them to a smaller set of primitives. 

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

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.

> 
>> 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.

Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to