Proposal: The Conditional Fail and Recover Operators; or: a more generic Existential Operator

Claude Pache claude.pache at
Thu May 22 14:58:37 PDT 2014


Today, I came to a case where I wanted to grab a reference to some getter of the DOM,
but to not fail if it doesn't exists:

try {
    originalElementsGetter = Object.getOwnPropertyDescriptor(window.HTMLFormElement.prototype, 'elements').get
catch(e) {
    originalElementsGetter = undefined

(If you are curious: the context is monkey-patching `<form>.elements`, so that it includes
the `<output>` elements of the form, for I've just discovered that IE forgets to include them.)

This seems to be a typical case for the Existential Operator (?.) we spoke recently about
(although, in my precise case, a try/catch does the trick).
However, the Existential Operator works only on properties and methods:

// Bad: if window.HTMLFormElement?.prototype is undefined, Object.getOwnPropertyDescriptor will protest loudly.
originalElementsGetter = Object.getOwnPropertyDescriptor(window.HTMLFormElement?.prototype, 'elements')?.get

// Here is what I could use:
Object.defineProperty(Object.prototype, '$getOwnPropertyDescriptor', {
    value: function(prop) { return Object.getOwnPropertyDescriptor(this, prop) }
  , writable: true
  , configurable: true
oldElementsGetter = window.HTMLFormElement?.prototype?.$getOwnPropertyDescriptor('elements')?.get

Can we do better?

I want to propose a mechanism that can simply express the following
(only partially fulfilled by the Existential Operator):

1. check for nully (null or undefined) values at precise spots of an expression being evaluated;
2. if a nully is found, stop the evaluation of the entire current (sub)expression, and yield undefined;
3. or, instead of yielding undefined, provide an alternate value;
4. and be able to determine precisely what "the current subexpression" of point 2 means.

So, let's introduce the Conditional Fail Operator (`??`), and, its counterpart, the Recover Operator (`!!`).
(You could think of them as a throw/catch-like mechanism, or, better, as a break/block-like mechanism.)
For the sake of illustration, I have artificially lengthen my original example:

originalElementsGetter = Object.getOwnPropertyDescriptor(window.HTMLFormElement??.prototype??, 'elements')??.get??
                          !! Object.getOwnPropertyDescriptor(window.HTMLElement??.prototype??, 'elements')??.get??
                          !! Object.getOwnPropertyDescriptor(window.Element??.prototype??, 'elements')??.get

The semantics are the following:

* `??` is a unary operator that is used as suffix. If its operand is nully (null or undefined),
    the evaluation of a certain expression (more precisely defined below) that it is part of, fails immediately.
    Otherwise, the operand is just returned as is.
* `!!` is a short-circuiting binary operator. It evaluates its LHS.
    If the evaluation is not interrupted by a `??`, it returns the result.
    But if the evaluation is interrupted by a `??`, it evaluates its RHS and returns the result.
* For any assignement operator (`=`, `+=`, etc.), if the evaluation of its RHS is interrupted by a `??`,
    the RHS is replaced by `undefined` and the assignment is performed normally.
* All other operators and function calls are transparent to the failure signal send by `??`
    (you have to stop it by an explicit `!!`).

As I have shown in [1], it seems relatively easy to spec such a behaviour using a custom Abrupt Completion.
The proposed spec needs to be refined in order to avoid precocious GetValue(...) calls,
but I am able to write a complete strawman in case there is interest.




More information about the es-discuss mailing list