While closing @bterlson also mentioned that all abstract operations return a completion value and therefore must use ? or !.
That does not seem consistent with the draft, e.g., IsCallable is never prefixed.
There should probably be some more reliable set of rules.
@annevk @bterlson
and also probably worth noting that abstraction operations used for static semantics specification can't produce abrupt completions because they happen outside of the ES runtime execution environment.
? or ! are not mandatory. If not used the assumption is that you're operating on a completion record (static semantics is the one exception I think?). There is a rule in 5.2 that allows us to treat a completion record as if it were a value but it's something I'd like to get away from. It's in that spirit that I closed #424.
However, I understand wanting to avoid ! when calling abstract operations that cannot throw. We should probably fix #253 and notate certain operations as non-throwing and specify that these return values not completion records. That would necessitate not using ! and ? on such operations. I'll add this to emu.
We should probably fix #253 and notate certain operations as non-throwing and specify that these return values not completion records.
(I have a related proposal, but it's waiting for #452 to be merged.)
@jmdyck so given the proposal in #497 we would remove ! for OrdinaryGetOwnProperty calls since that never returns an abrupt completion?
However, I understand wanting to avoid ! when calling abstract operations that cannot throw. We should probably fix #253 and notate certain operations as non-throwing and specify that these return values not completion records. That would necessitate not using ! and ? on such operations. I'll add this to emu.
Hmm, I do not like this approach. The reason I wanted #253 in the first place was so that I know, when writing other "code" that uses that abstract operation, whether I should use ! or ? at the call site, and consequently whether the call site itself can throw or not throw. Eliminating the ! from the call site reduces clarity, making me jump to the definition of the abstract operation to find out if we're assuming it returns a non-completion record, or a completion record.
I'd much rather have AbstractOp(x) always return a completion record, that must always be unwrapped via ! or ?. The extra character is a very minimal burden and adds a lot of good explictness.
@annevk:
so given the proposal in #497 we would remove ! for OrdinaryGetOwnProperty calls since that never returns an abrupt completion?
The proposal in #497 doesn't require the removal of the "!". You _could_ remove it, and the semantics would be the same (just as the semantics are the same if you remove an "Assert:"), but that doesn't mean it'd be a good idea. I think it'd be the editor's call.
@domenic:
I'd much rather have AbstractOp(x) always return a completion record, that must always be unwrapped via ! or ?.
It seems to me there are two intertwined ideas here:
I think these ideas are separable, and we can retain the good of the former while getting rid of (what at least some people believe is) the bad of the latter. #497 might be a way to do so.
I disagree that the ideas are separable. The latter is good, not bad.
Well, hm, you're not saying much to bolster that position. How about this: what benefit does the latter provide (that you don't already get from the former)?
A consistent type system that makes it easy to reason about algorithm flow.
Hm, interesting. Just to be clear, the type system you're talking about is not for ES, but for the pseudo-language in which ES is defined, right?
Correct. ! and ? are operators in this system.
Okay.
I need a short name for an underlying semantics in which every abstract op returns a Completion Record. For now, I'll just call it X. When you say that one of the benefits of adopting X would be a consistent type system, that suggests that without X, we do not have a consistent type system. Do you mean that in the sense of logical consistency? I.e., in the current type system, is it possible to arrive at two contradictory conclusions? Or are you talking about a different kind of consistency?
Right now my mental model aligns with Domenic's I think. Despite the fact that the completion record type presently doesn't allow certain abstract operations to return completion records because their return type is not an ECMAScript value (or empty), I think it's better to be more consistent in the sense that the same mental model can be applied to all invocations of abstract operations. Specifically, only static semantics for which a runtime exception mechanism makes no sense should return naked values and otherwise abstract ops should be understood to return completion records. Even static semantics I could see returning completion records considering they can throw errors, but they do so using a bulleted list of early error conditions rather than explicit algorithm steps.
For those who would like to see a '!' at every operation-invocation that does not return an abrupt completion, how about this:
Find every operation that cannot under any circumstances return an abrupt completion, and change its name to begin with '!' (or whatever you like that's distinctive).
Then
Let _x_ be !Foo(...)
means "!Foo is an operation that never returns an abrupt completion, and thus the result of this invocation is certainly not an abrupt completion", whereas
Let _x_ be ! Foo(...)
means "Foo is an operation that can, under some circumstances, return an abrupt completion; however, we are asserting that the circumstances of this invocation are such that it will not in this case".
So then the people who are interested in that distinction can pay attention to the space after the "!", and the people who aren't interested in the distinction can just ignore the space.
What is the usefulness of that distinction at the callsite? In both cases, an implementor knows that an exception won鈥檛 be thrown; why does it matter if it鈥檚 because of something inherent to the operation, or because of guarantees made by the arguments?
Because one is a simple (probably statically-verifiable) property of the operation, whereas the other requires reasoning to confirm. The distinction is probably more useful to the writers & reviewers of the spec than to implementors, though I can imagine an implementor being interested.
I don't think that would be a good idea. If you're interested in signaling properties of the operation, I'd suggest working on #253. In any case, I'd still prefer a consistent calling convention, so that whenever you call an operation without !/?, you get back a completion record.
I鈥檇 prefer that determining that an operation can鈥檛 ever throw always requires reasoning about the operation鈥檚 actual spec text, tbh.
The purpose of ?/! is to unwrap the completion record, and either ReturnIfAbrupt, or assert that the completion type is Normal - it鈥檚 not to convey inherent properties of the abstract operation.
If you're interested in signaling properties of the operation, I'd suggest working on #253.
I am, in #545.
I鈥檇 prefer that determining that an operation can鈥檛 ever throw always requires reasoning about the operation鈥檚 actual spec text, tbh.
I'd prefer that it be statically determinable, which I think it mostly is.
The purpose of ?/! is to unwrap the completion record
I don't subscribe to the view that every operation always returns a completion record (#496 + #497), so I disagree with you there.
it鈥檚 not to convey inherent properties of the abstract operation.
Okay.
Most helpful comment
Right now my mental model aligns with Domenic's I think. Despite the fact that the completion record type presently doesn't allow certain abstract operations to return completion records because their return type is not an ECMAScript value (or empty), I think it's better to be more consistent in the sense that the same mental model can be applied to all invocations of abstract operations. Specifically, only static semantics for which a runtime exception mechanism makes no sense should return naked values and otherwise abstract ops should be understood to return completion records. Even static semantics I could see returning completion records considering they can throw errors, but they do so using a bulleted list of early error conditions rather than explicit algorithm steps.