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

Claude Pache claude.pache at
Fri May 23 00:18:00 PDT 2014

Le 22 mai 2014 à 23:58, Claude Pache <claude.pache at> a écrit :

> Hi,
> 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:
> ```javascript
> 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:
> ```javascript
> // 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:
> ```javascript
> 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 `!!`).

I see a nasty footgun in the last two bullets, in case someone refactors from ` = baz??` to `fooMap.set('bar', baz??)`, for the latter will fail silently.
But we do want function calls to be transparent to  `??`-failure signals, as it can seen in my original example.
The best alternative I can see, is the following:

* All operators (excluding `!!`, of course, but including assignments) and function calls are transparent to failure signals.
* If a failure signal remains uncaught, an error is thrown (and this condition may even be statically determined and produce an early error).
    This will catch (!) early failed-assignment bugs, and force the programmer to be more explicit.

With this amendment, my example must be completed with a final `!! undefined`:

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


> 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.
> —Claude
> [1]

More information about the es-discuss mailing list