New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notate abstract ops that can and cannot throw #253

Open
domenic opened this Issue Dec 15, 2015 · 16 comments

Comments

Projects
None yet
8 participants
@domenic
Member

domenic commented Dec 15, 2015

In streams, which is heavily promise-based, there are a lot of abstract operations that cannot throw. The ES spec has these too, in smaller number.

Of course, this kind of nothrow vs. throw characteristic can be contagious. If you only use nothrow abstract ops in your abstract op, then your abstract op is nothrow. Whereas if you use any throwing ops, you are now throwing. Unless you are certain for external reasons that the given throw operation will not throw in this context, in which case you might want to assert that it is a non-abrupt-completion.

There are a couple action items here, some of which can be done independently:

  • Come up with a convention for asserting that the return value is never an abrupt completion, e.g. Let _result_ be ! AbstractOp(). Use it throughout. A lot of places in the spec currently use "perform" for this purpose, which is not defined anywhere.
  • Introduce EMU support for notating with throws/nothrow. (Or perhaps, abrupt/noabrupt.) You can see this in action in Streams, e.g. https://streams.spec.whatwg.org/#rs-abstract-ops. For the source format, I'd suggest either <emu-clause nothrow> or <emu-clause flair="nothrow">.
  • Go through the ES spec and update all abstract ops with whether or not they can throw. This could be done incrementally.
  • Build automated checker tools to enforce the contagion rules, based on throw/nothrow and ! or ?.
@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 15, 2015

Member

What about the case where the result of AbstractOp is not needed? That's usually the cases where "Perform" appears. Having a useless _result_ doesn't seem ideal. Do you have an alternative suggestion for an abstract op that solely causes side effects (some of which can throw, and some of which can't)?

In general I love this idea - being explicit about what can throw and what can't seems like an overall improvement.

Member

ljharb commented Dec 15, 2015

What about the case where the result of AbstractOp is not needed? That's usually the cases where "Perform" appears. Having a useless _result_ doesn't seem ideal. Do you have an alternative suggestion for an abstract op that solely causes side effects (some of which can throw, and some of which can't)?

In general I love this idea - being explicit about what can throw and what can't seems like an overall improvement.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Dec 15, 2015

Member

Do you have an alternative suggestion for an abstract op that solely causes side effects (some of which can throw, and some of which can't)?

1. ? AbstractOp() to bubble errors, or 1. ! AbstractOp() to assert it never errors. You could introduce the "perform" verb for that but I'd like it to be defined this time.

Member

domenic commented Dec 15, 2015

Do you have an alternative suggestion for an abstract op that solely causes side effects (some of which can throw, and some of which can't)?

1. ? AbstractOp() to bubble errors, or 1. ! AbstractOp() to assert it never errors. You could introduce the "perform" verb for that but I'd like it to be defined this time.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 15, 2015

Member

Sounds good - I like having an explicit "this is for side effects" mechanism like "Perform".

Member

ljharb commented Dec 15, 2015

Sounds good - I like having an explicit "this is for side effects" mechanism like "Perform".

@jmdyck

This comment has been minimized.

Show comment
Hide comment
@jmdyck

jmdyck Dec 15, 2015

Collaborator

Here's what I said when this was raised in the ecmarkup repo:

Presumably "Perform AbstractOp(x)" implies not just that AbstractOp doesn't throw, but also that it doesn't return anything useful (via normal completion). (Or at least, that these things are known to be true for the particular arguments being passed.)

I think it would be ideal if Ecmarkup supported this, e.g. <h1 aoid="..." nothrow>...</h1>.

To generalize your suggestion, I think it would be useful (though even more work) if every operation had a "header" that systematically listed/described the operation's inputs (parameters) and outputs (returns). (Currently, preambles give some of this info, but not consistently or completely.) E.g., outputs/returns might say something like:

normal returns: Boolean
abrupt returns: TypeError

So the throw/nothrow distinction would be captured by whether any abrupt returns are listed.

Collaborator

jmdyck commented Dec 15, 2015

Here's what I said when this was raised in the ecmarkup repo:

Presumably "Perform AbstractOp(x)" implies not just that AbstractOp doesn't throw, but also that it doesn't return anything useful (via normal completion). (Or at least, that these things are known to be true for the particular arguments being passed.)

I think it would be ideal if Ecmarkup supported this, e.g. <h1 aoid="..." nothrow>...</h1>.

To generalize your suggestion, I think it would be useful (though even more work) if every operation had a "header" that systematically listed/described the operation's inputs (parameters) and outputs (returns). (Currently, preambles give some of this info, but not consistently or completely.) E.g., outputs/returns might say something like:

normal returns: Boolean
abrupt returns: TypeError

So the throw/nothrow distinction would be captured by whether any abrupt returns are listed.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Dec 15, 2015

Member

@jmdyck: that would be amazing, but much work... I think we can probably get there eventually by going down the path of incrementally adding !.

Note that Perform X() is not about implying that AbstractOp doesn't throw (note further the many instances of Perform ? Op()). It's simply ecmaspeak for "the result of X()" where x doesn't have a useful result. In other words, with bang, if an abstract op doesn't have a useful result, we'd want Perform ! Op().

I fully support ! as a short-hand for "Assert: previous thing is not an abrupt completion". We can probably just start the incremental addition by doing a similar regexp replace as we did to kick off the ? conversion.

Member

bterlson commented Dec 15, 2015

@jmdyck: that would be amazing, but much work... I think we can probably get there eventually by going down the path of incrementally adding !.

Note that Perform X() is not about implying that AbstractOp doesn't throw (note further the many instances of Perform ? Op()). It's simply ecmaspeak for "the result of X()" where x doesn't have a useful result. In other words, with bang, if an abstract op doesn't have a useful result, we'd want Perform ! Op().

I fully support ! as a short-hand for "Assert: previous thing is not an abrupt completion". We can probably just start the incremental addition by doing a similar regexp replace as we did to kick off the ? conversion.

@zenparsing

This comment has been minimized.

Show comment
Hide comment
@zenparsing

zenparsing Dec 16, 2015

Contributor

I'm a little worried about extending the ECMASpeak programming language too fast. The RIA shorthand was a good buy in terms of readability/writability, but I'm not so sure about "!".

Contributor

zenparsing commented Dec 16, 2015

I'm a little worried about extending the ECMASpeak programming language too fast. The RIA shorthand was a good buy in terms of readability/writability, but I'm not so sure about "!".

@jmdyck

This comment has been minimized.

Show comment
Hide comment
@jmdyck

jmdyck Dec 16, 2015

Collaborator

@jmdyck: that would be amazing, but much work...

I'm prepared to do the work if we can agree on a "header" syntax.

Collaborator

jmdyck commented Dec 16, 2015

@jmdyck: that would be amazing, but much work...

I'm prepared to do the work if we can agree on a "header" syntax.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Dec 16, 2015

Member

I'm a fan of !. It fixes two important problems with the current spec:

  • There are many cases where a completion value v is used, but v.[[value]] is expected. For example, CreateDataProperty(array, ToString(n), e). ToString returns a completion value, but CreateDataProperty does not accept completion values as its second argument. This is just a bug in the current spec really. (Or if you prefer, an intentional type mismatch for the sake of brevity.)
  • There are several (39) places in the spec where we assert something is not an abrupt completion. This is a bit distracting, but is honestly probably something we should be doing more! It should be easy to say something is expected to never throw.

I am less of a fan of the header syntax, but some mock-up screenshots could possibly convince me.

Member

domenic commented Dec 16, 2015

I'm a fan of !. It fixes two important problems with the current spec:

  • There are many cases where a completion value v is used, but v.[[value]] is expected. For example, CreateDataProperty(array, ToString(n), e). ToString returns a completion value, but CreateDataProperty does not accept completion values as its second argument. This is just a bug in the current spec really. (Or if you prefer, an intentional type mismatch for the sake of brevity.)
  • There are several (39) places in the spec where we assert something is not an abrupt completion. This is a bit distracting, but is honestly probably something we should be doing more! It should be easy to say something is expected to never throw.

I am less of a fan of the header syntax, but some mock-up screenshots could possibly convince me.

@anba

This comment has been minimized.

Show comment
Hide comment
@anba

anba Dec 16, 2015

Contributor

ToString returns a completion value, but CreateDataProperty does not accept completion values as its second argument. This is just a bug in the current spec really. (Or if you prefer, an intentional type mismatch for the sake of brevity.)

No, it is a specified and documented feature of completion records to allow implicit access to the [[value]] field (cf. 6.2.2.2).

How about doing it the other way around: Explicitly state with ! when a caller needs to receive a completion record. I think that should be more ergonomic than adding ! after every infallible operation.

Contributor

anba commented Dec 16, 2015

ToString returns a completion value, but CreateDataProperty does not accept completion values as its second argument. This is just a bug in the current spec really. (Or if you prefer, an intentional type mismatch for the sake of brevity.)

No, it is a specified and documented feature of completion records to allow implicit access to the [[value]] field (cf. 6.2.2.2).

How about doing it the other way around: Explicitly state with ! when a caller needs to receive a completion record. I think that should be more ergonomic than adding ! after every infallible operation.

@rossberg

This comment has been minimized.

Show comment
Hide comment
@rossberg

rossberg Dec 16, 2015

Member

@anba, please no. The spec should not be optimised for maximum brevity but for maximum clarity. I think the implicit conversion from completion records to their values is actively harmful in that regard. There are already too many hidden conventions going on in the spec (a criticism I keep hearing from fellow V8 implementers trying to make sense of it every other day).

Member

rossberg commented Dec 16, 2015

@anba, please no. The spec should not be optimised for maximum brevity but for maximum clarity. I think the implicit conversion from completion records to their values is actively harmful in that regard. There are already too many hidden conventions going on in the spec (a criticism I keep hearing from fellow V8 implementers trying to make sense of it every other day).

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Dec 16, 2015

Member

No, it is a specified and documented feature of completion records to allow implicit access to the [[value]] field (cf. 6.2.2.2).

Yes, that is what I meant in the parenthetical.

Member

domenic commented Dec 16, 2015

No, it is a specified and documented feature of completion records to allow implicit access to the [[value]] field (cf. 6.2.2.2).

Yes, that is what I meant in the parenthetical.

@jmdyck

This comment has been minimized.

Show comment
Hide comment
@jmdyck

jmdyck Dec 16, 2015

Collaborator

I think the implicit conversion from completion records to their values is actively harmful in that regard.

I agree. (And also the reverse implicit conversion.)

I've thought of an approach that I think would eliminate most of these conversions. I'll raise it in another issue when I have some time.

Collaborator

jmdyck commented Dec 16, 2015

I think the implicit conversion from completion records to their values is actively harmful in that regard.

I agree. (And also the reverse implicit conversion.)

I've thought of an approach that I think would eliminate most of these conversions. I'll raise it in another issue when I have some time.

@allenwb

This comment has been minimized.

Show comment
Hide comment
@allenwb

allenwb Dec 16, 2015

Member

I want to support the general position expressed by @rossberg-chromium above.

Maximizing clarity (and particularly for occasional readers should be among the highest priorities. Most readers of the spec. are not spending 8 hours a day emerged within it. More likely they are occasionally looking up some specific details. They may or may not have read the clause 5 conversions and even if they have they may not remember them what they read.

@domenic things like CreateDataProperty(array, ToString(n), e) is used in situations where n is known to have a value that will convert to a string, without error. Most typically this is an integer valued index variable of a algorithmic loop. I considered making the conversion from completion to value explicit but found that formulation to be less readable.

Member

allenwb commented Dec 16, 2015

I want to support the general position expressed by @rossberg-chromium above.

Maximizing clarity (and particularly for occasional readers should be among the highest priorities. Most readers of the spec. are not spending 8 hours a day emerged within it. More likely they are occasionally looking up some specific details. They may or may not have read the clause 5 conversions and even if they have they may not remember them what they read.

@domenic things like CreateDataProperty(array, ToString(n), e) is used in situations where n is known to have a value that will convert to a string, without error. Most typically this is an integer valued index variable of a algorithmic loop. I considered making the conversion from completion to value explicit but found that formulation to be less readable.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Feb 18, 2016

Member

Going to close this issue as I believe we have satisfied the original request.

Member

bterlson commented Feb 18, 2016

Going to close this issue as I believe we have satisfied the original request.

@bterlson bterlson closed this Feb 18, 2016

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 18, 2016

Member

I don't believe we have. The first bullet point is mostly implemented, but the overall request motivating it (as outlined in the subsequent ones) remains open.

Member

domenic commented Feb 18, 2016

I don't believe we have. The first bullet point is mostly implemented, but the overall request motivating it (as outlined in the subsequent ones) remains open.

@domenic domenic reopened this Feb 18, 2016

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Feb 20, 2016

Member

Oh I see, sorry about that.

Member

bterlson commented Feb 20, 2016

Oh I see, sorry about that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment