Are Private name and Weak Map the same feature? and the Assoc API

David Bruant bruant.d at gmail.com
Fri Dec 16 03:19:17 PST 2011


Hi,

I couldn't post on es-discuss yesterday, so I rewrote my message and
posted it on github [1]. Here is the markdown source:

# PART 1: The same feature?
When looked abstractly, both private name and weak maps propose an
equivalent feature: a mapping between 2 unforgeable references and a value.
Both allow to share a secret with someone assuming the knowledge of 2
unforgeable entities.
Both have the interesting property of symmetric non-discoverability:

* Given an object, you can't list all private names
* Given a private name, you can't list all objects which have this name
as a property
* Given a weak map, you can't list all objects used as keys
* Given an object, you can't list all weak maps using this object as key

This can be used in WeakMaps to optimize garbage collection. As it turns
out, the same thing stands for private names: If nothing holds a
reference to a private name, all related slots in objects can be
garbage-collected as well.

There are a couple of differences:

*   A private property can be made non-configurable & non-writable
*   A private name can refer to an accessor property

I claim that these can be reimplemented by overriding the WeakMap API.

I'm open to discussion on whether there are cases that can be
implemented with one API and not the other. For the rest of this
message, I'll assume that it's the same feature with 2 different syntax.


# PART 2: A more generic syntax
Unfortunately, private names and WeakMaps (and maps and sets...) both
define a special new sort of object to work. What about an API that
would allow to associate any 2 objects and bind a "secret" value to the
association?

`````JavaScript
// In one part of the code
var o1 = {},
    o2 = {};
var a = Assoc(o1, o2);
a.set(37);
   
// ...
// In another component which has a reference to both o1 and o2
var myA = Assoc(o1, o2);
myA.get(); // 37
`````

In order to unseal a secret associated with 2 objects, you need a
reference to both, that's it. Exactly like with WeakMaps, exactly like
with private names.


## Part 2.1: where I pretend I can reimplement WeakMaps with the Assoc API

`````JavaScript
WeakMap = function(){
    var weakMapIdentity = {};
    var presence = {};
   
    return {
        get: function(o){
            return Assoc(weakMapIdentity, o).get();
        },
        set: function(o, val){
            Assoc(presence, o).set(true);
            return Assoc(weakMapIdentity, o).set(val);
        },
        has: function(o){
            return !!Assoc(presence, o).get();
        },
        delete = function(o){
            Assoc(presence, o).set(undefined);
        }
    }
}
`````

Interestingly, 2 identities are required to emulate weakmaps. One for
the values (weakMapIdentity) and one to consider presence of keys. In my
opinion, the WeakMap API should be separated into 2 APIs. One with
get/set and the other with add/remove/has which would find the natural
name of WeakSet.


## Part 2.2: where I go further and get a bit crazy

Some properties:

*  Assoc() === Assoc // why waste a reference? :-p
*  Assoc(o) !== o
*  Assoc(o) === Assoc(o)
*  Assoc(Assoc(o)) === Assoc(o)
*  Assoc(o, o) === Assoc(o)
*  Assoc(o1, o2) === Assoc(o2, o1)

The Assoc API could be extended with any number of arguments. The
resulting object represents the association of all objects passed as
arguments.

*  Assoc(o1, o2, o3) === Assoc(Assoc(o1, o2), o3) === Assoc(o1,
Assoc(o2, o3))
*  ...and these applies to any number of arguments.

Long story short, the Assoc function only cares about whether or not you
pass it the right references to unseal the secret.
And you should not care about passing the objects in the "right order".
If I want to keep a secret from you, I'll create an unforgeable
reference; I won't try to "obscure" it by not telling you the order in
which you should pass the objects to Assoc

Of course, since there is no discoverability of associations, all good
garbage collection optimizations are kept.


With association objects, I think we can have a uniform base to play
with objects without having to invent new sorts of objects (WeakMaps,
Sets, Maps, etc.) while keeping the possibility to implement (hopefully
as efficiently) these abstractions.



I've had some comments from Erik Corry on Twitter.
[2] "Can you add a private property to a frozen object?"
=> This is not discussed in the proposal [4]. I would guess not. It may
be a difference indeed between WeakMaps and private names, but I think
it'd be possible to enhance the WeakMap API to allow either that a
weakmap stops accepting keys or that an object is banned from all
weakmaps (which are 2 different view of non-extensibility)
I forgot to say that the Assoc API was a first shot and of course some
additional primitives may be handy. This is what I'd like to explore.


[3] "For private names you can make a polyfill based on ES5
non-enumerability and random strings. Can u do that with Assoc?"
=> The polyfill would be only partial, because properties would appear
anyway with Object.getOwnPropertyNames which defeats the purpose of
private names.
Moreover, there would always be a risk of collision (which can't happen
with private names thanks to the unforgeability property).
Private names have been added because they add something that lack to
the language. The Assoc API would do the same and any polyfill could
only be partial (same goes for WeakMaps)

David

[1] https://gist.github.com/1483744
[2] https://twitter.com/#!/erikcorry/status/147589242246275073
[3] https://twitter.com/#!/erikcorry/status/147588955611729920
[4] http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects


More information about the es-discuss mailing list