Tom Van Cutsem (2013-07-30T08:48:19.000Z)
tl;dr:

I would argue that Array.isArray should return true for proxies-for-arrays.
The other built-ins are less crucial and could stay the way they are.


Summarizing the previous discussion:
- I agree that it is not safe to generalize: a proxy for an X is not in
general substitutable for X.
- It follows that having proxies-for-X pass all tests of the form "if O is
an exotic X" is undesirable.
- Hence, we should look at each of the tests and decide on a case-by-case
basis whether proxies can be permitted.

For the specific case of arrays, let's go over the list again:

> * Array.isArray
> We've discussed the meaning of this in the past and have agreed that it
> should be considered a test for exotic array-ness and in particular the
> length invariant. A Proxy whose target is an exotic array may or may not
> qualify. We could add a @@isArray tag, but I'm not convinced that it is
> really needed.
>

David's point about membranes being broken if Array.isArray(membranedArray)
returns false is a valid point. It means code outside a membrane will
observe all arrays inside the membrane as ordinary objects. This breaks
transparency quite badly.

Even with a custom proxy handler, membranes would not be able to fix this,
unless the membrane abstraction itself patches Array.isArray to recognize
its own membrane wrappers. The problem is not that array generics won't
work on wrapped arrays, it's that wrapped arrays have lost their "isArray"
brand.

As far as I can tell, the only real array invariant is
that Array.isArray(obj) currently implies that obj must have a
non-configurable data property named "length" that holds a number.

But we're in luck: since "length" is non-configurable on arrays, and since
direct proxies were carefully specced such that they could not violate
non-configurability of target properties, a proxy-for-array will be obliged
to return a valid "length" value as well. Hence having
Array.isArray(proxyForArray) return true makes sense.

Having a user-customizable @@isArray tag seems a bridge too far because
then we'd lose the invariant that Array.isArray tests for. But having
Array.isArray return true for exotic arrays *and only* proxies whose target
is an exotic array seems both safe and useful.


> > * Array.prototype.concat
> As currently spec'ed the [[Class]] test has been replaced with an exotic
> array test on the this value (supporting creating subclass instances) and a
> IsConcatSpreadable abstract operation (defined in terms of
> @@isConcatSpreadable) falling back to an exotic array test) used to decide
> whether to spread argument values.
> To make concat spread a proxied array you would have to explicitly define
> the @@isConcatSpreadable property of the target object.  To maintain legacy
> computability with objects inheriting from Array.prototype we can't just
> have include @@isConcatSpreadable on Array.prototype.
> There may be other teaks we could do.  But we talked about concat at last
> weeks meeting WRT Typed Arrays and concluded that concat is pretty bogus
> and it may be a turd that is not worth polishing.
>

Sorry, I was looking at jorendorff's online ES6 draft which did not include
this extensibility hook yet. It seems the @@isConcatSpreadable hook is
sufficient for proxies-for-arrays to allow themselves to be spread. Good!

> * JSON.stringify
> There are two distinct use cases of [[Class]]==Array in the ES5 spec of
> this function.  Both are currently in the ES6 spec. as  exotic array tests.
>  The first use case is to see if the "replaceer" argument is an white-list
> array.  This could be special cased via a @@isJSONArray that would work
> through a proxy, but  I dubious that the additional complexity is justified.
>

I agree. This would be a rare use case, and the issue is broader than just
proxies anyway (e.g. allowing any iterable).


> The other use case is when deciding whether to use { } or [ ] notation to
> encode the object.  The exotic array test  is the right one for ES5
> compatibility.   The appropriate JSON encoding  of a Proxy that has an
> exotic array target is going to be application dependent.   JSON.parse
> already has an extensibility hook (toJSON) that presumably should be
> sufficient to deal with such objects.
>

I didn't think of the toJSON() hook. That should work, at the cost of
giving up some transparency since `[].toJSON` returns undefined while
`proxyForArray.toJSON` would need to return a function.


> > * JSON.parse
> I believe that this [[Class]]==Array (now is exotic array) test is only
> applied to objects created by evaluating the JSON text as if it was an
> object literal.  Such objects will never be proxies.
>

Agreed.


> > * ArrayBuffer.isView
> yup.  The use cases for isView aren't all that clear to me.  It could be
> expressed a @@isView test if it has important use cases.


I'm not familiar enough with ArrayBuffers to understand the consequences.
By analogy with Array.isArray, if a proxy-for-arraybuffer automatically
upholds all observable invariants of ArrayBuffers, then arguably the test
should return true for proxies. What would be the observable invariants of
an ArrayBuffer?

Cheers,
Tom
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130730/e8fe922f/attachment.html>
domenic at domenicdenicola.com (2013-08-04T22:48:55.314Z)
tl;dr:

I would argue that Array.isArray should return true for proxies-for-arrays.
The other built-ins are less crucial and could stay the way they are.


Summarizing the previous discussion:

- I agree that it is not safe to generalize: a proxy for an X is not in general substitutable for X.
- It follows that having proxies-for-X pass all tests of the form "if O is an exotic X" is undesirable.
- Hence, we should look at each of the tests and decide on a case-by-case basis whether proxies can be permitted.

For the specific case of arrays, let's go over the list again:

>> * Array.isArray
>
> We've discussed the meaning of this in the past and have agreed that it
> should be considered a test for exotic array-ness and in particular the
> length invariant. A Proxy whose target is an exotic array may or may not
> qualify. We could add a @@isArray tag, but I'm not convinced that it is
> really needed.

David's point about membranes being broken if Array.isArray(membranedArray)
returns false is a valid point. It means code outside a membrane will
observe all arrays inside the membrane as ordinary objects. This breaks
transparency quite badly.

Even with a custom proxy handler, membranes would not be able to fix this,
unless the membrane abstraction itself patches Array.isArray to recognize
its own membrane wrappers. The problem is not that array generics won't
work on wrapped arrays, it's that wrapped arrays have lost their "isArray"
brand.

As far as I can tell, the only real array invariant is
that Array.isArray(obj) currently implies that obj must have a
non-configurable data property named "length" that holds a number.

But we're in luck: since "length" is non-configurable on arrays, and since
direct proxies were carefully specced such that they could not violate
non-configurability of target properties, a proxy-for-array will be obliged
to return a valid "length" value as well. Hence having
Array.isArray(proxyForArray) return true makes sense.

Having a user-customizable @@isArray tag seems a bridge too far because
then we'd lose the invariant that Array.isArray tests for. But having
Array.isArray return true for exotic arrays *and only* proxies whose target
is an exotic array seems both safe and useful.


>> * Array.prototype.concat
>
> As currently spec'ed the [[Class]] test has been replaced with an exotic
> array test on the this value (supporting creating subclass instances) and a
> IsConcatSpreadable abstract operation (defined in terms of
> @@isConcatSpreadable) falling back to an exotic array test) used to decide
> whether to spread argument values.
> To make concat spread a proxied array you would have to explicitly define
> the @@isConcatSpreadable property of the target object.  To maintain legacy
> computability with objects inheriting from Array.prototype we can't just
> have include @@isConcatSpreadable on Array.prototype.
> There may be other teaks we could do.  But we talked about concat at last
> weeks meeting WRT Typed Arrays and concluded that concat is pretty bogus
> and it may be a turd that is not worth polishing.

Sorry, I was looking at jorendorff's online ES6 draft which did not include
this extensibility hook yet. It seems the @@isConcatSpreadable hook is
sufficient for proxies-for-arrays to allow themselves to be spread. Good!

>> * JSON.stringify
>
> There are two distinct use cases of [[Class]]==Array in the ES5 spec of
> this function.  Both are currently in the ES6 spec. as  exotic array tests.
>  The first use case is to see if the "replaceer" argument is an white-list
> array.  This could be special cased via a @@isJSONArray that would work
> through a proxy, but  I dubious that the additional complexity is justified.
>

I agree. This would be a rare use case, and the issue is broader than just
proxies anyway (e.g. allowing any iterable).


> The other use case is when deciding whether to use { } or [ ] notation to
> encode the object.  The exotic array test  is the right one for ES5
> compatibility.   The appropriate JSON encoding  of a Proxy that has an
> exotic array target is going to be application dependent.   JSON.parse
> already has an extensibility hook (toJSON) that presumably should be
> sufficient to deal with such objects.

I didn't think of the toJSON() hook. That should work, at the cost of
giving up some transparency since `[].toJSON` returns undefined while
`proxyForArray.toJSON` would need to return a function.


>> * JSON.parse
>
> I believe that this [[Class]]==Array (now is exotic array) test is only
> applied to objects created by evaluating the JSON text as if it was an
> object literal.  Such objects will never be proxies.
>

Agreed.


>> * ArrayBuffer.isView
>
> yup.  The use cases for isView aren't all that clear to me.  It could be
> expressed a @@isView test if it has important use cases.


I'm not familiar enough with ArrayBuffers to understand the consequences.
By analogy with Array.isArray, if a proxy-for-arraybuffer automatically
upholds all observable invariants of ArrayBuffers, then arguably the test
should return true for proxies. What would be the observable invariants of
an ArrayBuffer?