preventExtensions trap and its true/false protocol

# David Bruant (12 years ago)

The current proxy_spec [1], for the Object.preventExtensions built-in, suggests to read the result of the preventExtensions trap as a boolean and throw if falsy. From the developer perspective, this doesn't really add anything since it's already possible to throw from within the trap (and that's probably more explicit and clearer than returning false). This was pretty much agreed on in another recent thread (end of [2], then [3][4][5]). Is it possible to remove this part? In the end, the trap return value would be just ignored.

David

[1] harmony:proxies_spec#changes_to_es5_built-in_functions [2] esdiscuss/2013-January/027999 [3] esdiscuss/2013-January/028000 [4] esdiscuss/2013-January/028001 [5] esdiscuss/2013-January/028122

# Jeff Walden (12 years ago)

On 03/31/2013 11:02 AM, David Bruant wrote:

From the developer perspective, this doesn't really add anything since it's already possible to throw from within the trap (and that's probably more explicit and clearer than returning false).

That puts the onus on the trap to throw the correct kind of error, right? Admittedly this should be easy enough to get right, but it is a slight bit of extra complexity, versus having the implementation throw the correct error in one central location.

# David Bruant (12 years ago)

Le 31/03/2013 21:17, Jeff Walden a écrit :

On 03/31/2013 11:02 AM, David Bruant wrote:

From the developer perspective, this doesn't really add anything since it's already possible to throw from within the trap (and that's probably more explicit and clearer than returning false). That puts the onus on the trap to throw the correct kind of error, right?

What do you mean by "correct"? It's already possible to throw any sort of error from any trap; I'm not sure what's correct and what isn't in that context.

Admittedly this should be easy enough to get right, but it is a slight bit of extra complexity, versus having the implementation throw the correct error in one central location.

I think the difference is at best marginal. As a reader of people writing JS code, I'd rather have people explicitly throwing than having to remember which trap has a true/false protocol. As a web developer, I'm happy if the surface of things to test for web browser implementors is smaller ;-) (and that's going to be a net reduction in terms of spec and I guess in implementations if the trap result is just ignored)

# Tom Van Cutsem (12 years ago)

The messages you cite discuss the return value of the oddball "unknownPrivateSymbol" trap.

For the preventExtensions trap I see no reason to be inconsistent with the "set" and "defineProperty" traps.

Also, Reflect.preventExtensions (and Reflect.set, Reflect.defineProperty) will make the boolean trap return value directly visible and usable.

Personally, I find code like:

if (Reflect.preventExtensions(obj)) { // success code } else { // failure code }

cleaner than:

try { Object.preventExtensions(obj); // success code } catch (e) { // failure code }

Moreover, in the latter piece of code, we may accidentally catch the wrong exception.

In short, I don't see what's wrong with the boolean return value protocol. I find it much cleaner than the throw/return protocol. In addition, boolean return values can be more easily combined using the boolean operators. throw/return exit "signals" can't be combined that straightforwardly.

Cheers, Tom

2013/4/1 David Bruant <bruant.d at gmail.com>

# David Bruant (12 years ago)

Le 01/04/2013 17:22, Tom Van Cutsem a écrit :

Hi David,

The messages you cite discuss the return value of the oddball "unknownPrivateSymbol" trap.

I assumed the comments applied to true/false protocols in general, but I'm interested if there is disagreement on that point.

For the preventExtensions trap I see no reason to be inconsistent with the "set" and "defineProperty" traps.

Historically, the internal operations these relate to needed to return a boolean and a decision on throwing or not was made based on strictness of the code. This isn't true for Object.preventExtensions.

Also, Reflect.preventExtensions (and Reflect.set, Reflect.defineProperty) will make the boolean trap return value directly visible and usable.

Personally, I find code like:

if (Reflect.preventExtensions(obj)) {

Note that Object.preventExtensions/seal/freeze do not return a boolean, but the object being manipulated. Is this an expected inconsistency? I hadn't given a lot of thoughts to it, but that could make a lot of sense.

// success code } else { // failure code }

cleaner than:

try { Object.preventExtensions(obj); // success code } catch (e) { // failure code }

Moreover, in the latter piece of code, we may accidentally catch the wrong exception.

That's what has been necessary to detect whether it was possible to freeze an host object or not (that is, detecting if it worked). In any case, since an error can be thrown anyway from the trap (and the error won't be swallowed), doing a try/catch may still be necessary.

# Brandon Benvie (12 years ago)

On 3/31/2013 4:38 PM, David Bruant wrote:

Le 31/03/2013 21:17, Jeff Walden a écrit :

On 03/31/2013 11:02 AM, David Bruant wrote:

From the developer perspective, this doesn't really add anything since it's already possible to throw from within the trap (and that's probably more explicit and clearer than returning false). That puts the onus on the trap to throw the correct kind of error, right? What do you mean by "correct"? It's already possible to throw any sort of error from any trap; I'm not sure what's correct and what isn't in that context.

The developer probably won't know what the correct error to throw is, assuming their goal is to fail exactly like Object.preventExtensions does. That is, the error that will be thrown from Object.preventExtensions is only specified as a TypeError; different engines can (and often do) have different error messages, and different localizations will also have different messages. This is something a developer can't hope to mimic.

That and throwing isn't always the correct action when the preventExtension trap fail (Reflect.preventExtensions). Boolean is definitely the right thing for this trap to return.

# Jeff Walden (12 years ago)

On 04/01/2013 09:41 AM, Brandon Benvie wrote:

That is, the error that will be thrown from Object.preventExtensions is only specified as a TypeError; different engines can (and often do) have different error messages, and different localizations will also have different messages. This is something a developer can't hope to mimic.

Note that this could also be construed as a feature, in certain cases. The "dead object" proxy Mozilla has, previously mentioned here (can't find a link), has all its traps throw an error with a message saying "Dead object". If the trap returns true/false, it's no longer possible for preventExtensions to say that the object in question is dead, not just can't be made non-extensible.

Now, whether this use case, and other cases we could conjure up with a little thought, actually motivate reserving the throw to the trap -- that's another matter. I'm just saying there are some small tradeoffs both ways here.

# Tom Van Cutsem (12 years ago)

2013/4/1 Jeff Walden <jwalden+es at mit.edu>

Note that this could also be construed as a feature, in certain cases. The "dead object" proxy Mozilla has, previously mentioned here (can't find a link), has all its traps throw an error with a message saying "Dead object". If the trap returns true/false, it's no longer possible for preventExtensions to say that the object in question is dead, not just can't be made non-extensible.

The true/false return protocol doesn't prevent exceptions to be thrown from the trap. It's perfectly fine for the dead object proxy's preventExtensions trap to throw an "object is dead" exception. In fact, that's precisely what would happen if one would call preventExtensions on a revoked revocable proxy.

# Jeff Walden (12 years ago)

On 04/02/2013 12:13 AM, Tom Van Cutsem wrote:

The true/false return protocol doesn't prevent exceptions to be thrown from the trap. It's perfectly fine for the dead object proxy's preventExtensions trap to throw an "object is dead" exception. In fact, that's precisely what would happen if one would call preventExtensions on a revoked revocable proxy.

Ah, yes, that's exactly right. Never mind!