arthurfiorette / proposal-safe-assignment-operator

Draft for ECMAScript Error Safe Assignment Operator
https://arthur.run/proposal-safe-assignment-operator/
MIT License
1.13k stars 12 forks source link

Unexpected parsing inconsistency #38

Open alray2569 opened 2 weeks ago

alray2569 commented 2 weeks ago

Whilst discussing #35, it came to my attention that based on the preprocessor examples in the proposal, the parsing of the ?= operator would be inconsistent with the way that JavaScript is currently parsed.

As a general rule, expressions in JavaScript are contiguous; that is, nowhere in the language do we allow an operator to directly impact a subexpression to which it is not adjacent. Furthermore, an operator always has exactly once precedence and is only evaluated once. The ?= operator breaks both of these rules.

According to the proposal, the ?= "operator is compatible with promises, async functions, and any value that implements the Symbol.result method." However, looking through examples, we can see that the ?= operator is not always adjacent to one of these things. For example, in the following example from the proposal, the ?= operator can only be interpreted as being adjacent to a value of type [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response):

const [error, response] ?= await fetch("https://arthur.place")

If we try to parse this expression as if it used = instead, it would parse like this:

const [error, response] ?= (await (fetch("https://arthur.place")))

// reduced to types
const [error, response] ?= (await (fetch(string))) // fetch(string) => Promise<Response>
const [error, response] ?= (await Promise<Response>) // await Promise<Response> => Response
const [error, response] ?= Response // !!! Response isn't a valid right side for ?=, so a TypeError is thrown!

So the ?= operator needs to "pierce through" the await operator to get to an acceptable RH value. This means that ?= can't parse the same as =. Further, the preprocessor examples show that the ?= operator should also pierce function application! So the real parse is this:

                        ?        fetch
                                      ("https://arthur.place")
                           await
const [error, response]  =

// fetch ~ string => Promise<Response>
// "..." ~ string
                        ?        string => Promise<Response>
                                                            (string)
                           await
const [error, response]  =

// Step 1: Safe-ify the `fetch` function
                                 string => SafePromise<[Error, Response]>(string)
                           await
const [error, response]  =

// Step 2: Evaluate the safe async function
                           await SafePromise<[Error, Response]>
const [error, response]  =

// Step 3: Await the safe promise
const [error, response]  = [Error, Response]

Notice that not only are we parsing in noncontiguous sections, but both step 1 and step 4 are tied to the safe-assignment operator. There is nothing else in JavaScript that requires this sort of nonlinear or double-parsing.