David’s ProxyMap

# Axel Rauschmayer (13 years ago)

gist.github.com/3918227

I’m wondering what this approach does better. Given the 3 objects-as-maps pitfalls [1], we have:

  1. Accidentally accessing inherited properties: fixed
  2. Can’t safely invoke methods, because those might be overridden: still a problem (right?)
  3. proto: fixed

[1] www.2ality.com/2012/01/objects-as-maps.html

# David Bruant (13 years ago)

2012/10/20 Axel Rauschmayer <axel at rauschma.de>

gist.github.com/3918227

I’m wondering what this approach does better.

Originally, it really was to give some (built-in) syntactic sugar to (Weak)Map. No plan to make objects better. At most sharing a simpler object model than the ES5 one.

Given the 3 objects-as-maps pitfalls [1], we have:

  1. Accidentally accessing inherited properties: fixed
  2. Can’t safely invoke methods, because those might be overridden: still a problem (right?)
  3. proto: fixed

I see what I've written as maps with sugar, not really as objects. maps-as-objects if you will :-) Specifically, calling "methods" makes almost no sense... Maybe it does actually. Interesting. I need to give it more thoughts. About your second point, it assumes that we want all objects to have Object.prototype methods. I'm not so sure it should be a goal. I'm glad it's possible to not inherit from Object.prototype as well as being able to shadow properties. About maps-as-objects, they have no inheritance, so I'm not sure the second point applies to it.

About the third point, maps-as-objects are indeed better, because all problems with pseudo-properties are avoided without sacrifying syntax (which all other solutions have to do).

# Axel Rauschmayer (13 years ago)
  1. Accidentally accessing inherited properties: fixed
  2. Can’t safely invoke methods, because those might be overridden: still a problem (right?)
  3. proto: fixed [...] About your second point, it assumes that we want all objects to have Object.prototype methods. I'm not so sure it should be a goal.

It’s loosely related to Allen’s object model reformation [1]: you’d want the [] “operator” to get and set collection elements, but would still want to be able to invoke map-related methods. Currently, the two are mutually exclusive.

Another idea: one could use the in operator to check whether a key exists in the map (only for ProxyMap at the moment, possibly for all collections in the future).

Currently, proxies make no distinction between a property read access and a method invocation. In my experience, it would be nice if that distinction would be there – if only that one didn’t have to curry for method invocations which must be a performance issue and is a fairly common use case (remotely invoking web services etc.). Now, there are reasons against this and I’m mainly wondering if actually using the new API has changed your or Tom’s mind.

Axel

[1] strawman:object_model_reformation

# David Bruant (13 years ago)

2012/10/20 Axel Rauschmayer <axel at rauschma.de>

  1. Accidentally accessing inherited properties: fixed
  1. Can’t safely invoke methods, because those might be overridden: still a problem (right?)
  2. proto: fixed

[...] About your second point, it assumes that we want all objects to have Object.prototype methods. I'm not so sure it should be a goal.

It’s loosely related to Allen’s object model reformation [1]: you’d want the [] “operator” to get and set collection elements, but would still want to be able to invoke map-related methods. Currently, the two are mutually exclusive.

I see what you mean. In my case, I have completely ditched method invocation. I have replaced it with syntactic sugar for some methods, but it may not be applicable for all Map.prototype methods.

Another idea: one could use the in operator to check whether a key exists in the map (only for ProxyMap at the moment, possibly for all collections in the future).

This is not possible, because maps are objects and have own properties too. In Firefox Nightly:

var m = new Map(); m.azerty = 123; // own property of m, not a map internal key/value pair console.log(m.azerty, m.get('azerty')) // 123, undefined

I find this snippet and the result consistent with my expectations.

Currently, proxies make no distinction between a property read access and a

method invocation. In my experience, it would be nice if that distinction would be there – if only that one didn’t have to curry for method invocations which must be a performance issue and is a fairly common use case (remotely invoking web services etc.).

I'm not following. Can you provide an example, please?

Now, there are reasons against this and I’m mainly wondering if actually using the new API has changed your or Tom’s mind.

Since in the object model reformation proposal, @elementGet & friends are symbols (unique I would assume since there is no need for them to be private), I think there is not a single line to change to the proxy proposal as it is now to make it work properly with the object model reformation proposal. If that's the case, it means that assuming the object model reformation passes, it'll be possible for proxies to separate property access from method invocation. Can Allen, Tom or Mark (or anyone familiar enough with both proposals) confirm?

# Axel Rauschmayer (13 years ago)

Another idea: one could use the in operator to check whether a key exists in the map (only for ProxyMap at the moment, possibly for all collections in the future). This is not possible, because maps are objects and have own properties too. In Firefox Nightly:

var m = new Map(); m.azerty = 123; // own property of m, not a map internal key/value pair console.log(m.azerty, m.get('azerty')) // 123, undefined

I find this snippet and the result consistent with my expectations.

I was thinking about ProxyMap. And my bad – it’s already there (the has method of the handler). So you can do:

var pm = new ProxyMap(); pm["key"] = "value"; console.log("key" in pm); // true console.log("toString" in pm); // false

Currently, proxies make no distinction between a property read access and a method invocation. In my experience, it would be nice if that distinction would be there – if only that one didn’t have to curry for method invocations which must be a performance issue and is a fairly common use case (remotely invoking web services etc.). I'm not following. Can you provide an example, please?

For remote method invocation and for things like a “method missing” handler, you have to use the following pattern: let handler = { get(target, name, receiver) { return (...args) => { // send name and args over the wire } } };

If methods were separate from property read accesses, no currying would be necessary here, e.g.:

let handler = {
    callMethod(target, name, receiver, args) {
        // send name and args over the wire
    }
};

For ProxyMap, you could allow getting and setting elements in addition to the invocation of map methods. That’s only possibly if the distinction is made.

# Tom Van Cutsem (13 years ago)

2012/10/20 Axel Rauschmayer <axel at rauschma.de>

Currently, proxies make no distinction between a property read access and a method invocation. In my experience, it would be nice if that distinction would be there – if only that one didn’t have to curry for method invocations which must be a performance issue and is a fairly common use case (remotely invoking web services etc.). Now, there are reasons against this and I’m mainly wondering if actually using the new API has changed your or Tom’s mind.

I agree there are use cases for distinguishing method invocations from property accesses (remote method calls are one of them -- you'd want to distinguish between doing an HTTP GET vs POST). But the new API hasn't changed the balance for or against an "invoke" trap. Recall that one of the reasons was that an "invoke" trap would lead to invoke-only methods, which goes against functional programming patterns in Javascript (e.g. people expect array.map(obj.method) to work)

# Yehuda Katz (13 years ago)

On Wed, Oct 24, 2012 at 2:56 PM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

2012/10/20 Axel Rauschmayer <axel at rauschma.de>

Currently, proxies make no distinction between a property read access and a method invocation. In my experience, it would be nice if that distinction would be there – if only that one didn’t have to curry for method invocations which must be a performance issue and is a fairly common use case (remotely invoking web services etc.). Now, there are reasons against this and I’m mainly wondering if actually using the new API has changed your or Tom’s mind.

I agree there are use cases for distinguishing method invocations from property accesses (remote method calls are one of them -- you'd want to distinguish between doing an HTTP GET vs POST). But the new API hasn't changed the balance for or against an "invoke" trap. Recall that one of the reasons was that an "invoke" trap would lead to invoke-only methods, which goes against functional programming patterns in Javascript (e.g. people expect array.map(obj.method) to work)

I'm not sure I understand the benefit of making it easy to develop APIs where foo.bar() is not roughly equivalent to (x = foo.bar).apply(foo). Am I misunderstanding something?

# Tom Van Cutsem (13 years ago)

2012/10/24 Yehuda Katz <wycats at gmail.com>

I'm not sure I understand the benefit of making it easy to develop APIs where foo.bar() is not roughly equivalent to (x = foo.bar).apply(foo). Am I misunderstanding something?

No, that's indeed another way of phrasing it. Proxies don't support invoke() in part because we didn't want to encourage such APIs.

# Andrea Giammarchi (13 years ago)

But then why such "encouragement" brendaneich.com/2012/10/harmony-of-dreams-come-true ? ( Proxy paragraph )

If noSuchMethod is wrong, what's the point of suggesting a way to simulate it through proxies?

Moreover, what's the point to mark it wrong if many developers asked for it?

I also remember I have written this a while ago: webreflection.blogspot.com/2011/12/please-give-us-back-nosuchmethod.html

As result I see Tom's implementation with bound callbacks per property and a freaking slower runtime every time an API would like a fancy noSuchMethod behavior ... just saying :-)

br

# Yehuda Katz (13 years ago)

On Wed, Oct 24, 2012 at 5:16 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

But then why such "encouragement" brendaneich.com/2012/10/harmony-of-dreams-come-true ? ( Proxy paragraph )

If noSuchMethod is wrong, what's the point of suggesting a way to simulate it through proxies?

noSuchMethod isn't the same problem as my concern about invoke-only traps. In this case, (x = sink.bar).apply(sink) would still hit the noSuchMethod method.

# Brendan Eich (13 years ago)

Yehuda Katz wrote:

On Wed, Oct 24, 2012 at 5:16 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com <mailto:andrea.giammarchi at gmail.com>> wrote:

But then why such "encouragement"
https://brendaneich.com/2012/10/harmony-of-dreams-come-true/ ? (
Proxy paragraph )

If __noSuchMethod__ is wrong, what's the point of suggesting a way
to simulate it through proxies?

noSuchMethod isn't the same problem as my concern about invoke-only traps. In this case, (x = sink.bar).apply(sink) would still hit the noSuchMethod method.

That's right.

Andrea, please see especially this part of the blog post you cited:

""" This implementation does not just call the |noSuchMethod| hook when a missing method is invoked, as shown after the |obj.bar()| line above. It also creates a thunk for any |get| of a property not in the target object and not in |Object.prototype|:

|js> obj.bar (function (...args) { return receiver.noSuchMethod(name, args); }) js> var thunk= obj.bar

js> thunk() "bar"|

I think this is an improvement on my original bugzilla.mozilla.org/show_bug.cgi?id=196097 |noSuchMethod|

creation all those years ago in SpiderMonkey. """

# Axel Rauschmayer (13 years ago)

I agree there are use cases for distinguishing method invocations from property accesses (remote method calls are one of them -- you'd want to distinguish between doing an HTTP GET vs POST). But the new API hasn't changed the balance for or against an "invoke" trap. Recall that one of the reasons was that an "invoke" trap would lead to invoke-only methods, which goes against functional programming patterns in Javascript (e.g. people expect array.map(obj.method) to work)

Ah, that makes sense. I previously didn’t understand why invoke-only methods were a problem.

In some ways, invoke-only methods do reflect how things actually work with proxies: they are easier to implement if you don’t have to reify a method. With the current API, you are forced to reify.

Doesn’t the currying incur a cost (for the frequent use case of “virtual methods”)? Could that cost (performance + elegance) be avoided in some other manner?

Axel

# Axel Rauschmayer (13 years ago)

I'm not sure I understand the benefit of making it easy to develop APIs where foo.bar() is not roughly equivalent to (x = foo.bar).apply(foo). Am I misunderstanding something?

Right. Keeping that invariant is important.

I’d like to avoid (the cognitive and performance cost of) having to curry whenever you implement methods via a proxy (which seems the most important use case for proxies).

Being able to distinguish between a property read access and a method invocation (which, spec-internally, you can do via a reference) is indeed rarely interesting. It could be used for ProxyMap, but that seems iffy, design-wise:

let pm = new ProxyMap(); pm.size = 123; // create a map entry pm.size(); // invoke the size() method

# David Bruant (13 years ago)

Le 25/10/2012 12:42, Axel Rauschmayer a écrit :

I'm not sure I understand the benefit of making it easy to develop APIs where foo.bar() is not roughly equivalent to (x = foo.bar).apply(foo). Am I misunderstanding something?

Right. Keeping that invariant is important.

I’d like to avoid (the cognitive and performance cost of) having to curry whenever you implement methods via a proxy (which seems the most important use case for proxies).

Being able to distinguish between a property read access and a method invocation (which, spec-internally, you can do via a reference) is indeed rarely interesting. It could be used for ProxyMap, but that seems iffy, design-wise:

let pm = new ProxyMap(); pm.size = 123; // create a map entry pm.size(); // invoke the size() method

I've thought about .size and .keys, .values, etc. The sugar I added doesn't support these (well, .keys can be sugared with the keys/enumerate traps :-p). Everything may be doable with ProxyMap static methods. I have not given myself the goal to emulate all Map/WeakMap capabilities. But when one just wants a key->value store with object

syntax (get, set, delete, 'in'), ProxyMap does the job.

# Andrea Giammarchi (13 years ago)

this

(x = sink.bar).apply(sink)

to me is like using apply to undefined ... that is not the equivalent of an "invoke" where the context should be explicit ( e.g. obj.method() ) so is not, in my opinion, a good noSuchMethod case.

sink.bar is a getter , if not there, is undefined (unless chained down the proto) as it is for any property

sink.bar(); is an invoke

AFAIK noSuchMethod as never been used as mixin ... I cannot even think about it, but it's possible to recycle the callback for many objects so no need to be able to extract/assign it, imho

Not having it and forcing it through a different pattern is not the same as having it maybe not 100% perfect ... and I believe latter is also what made JS great in the past.

Anyway, I understand nobody wants it so I won't insist any further.

br