tc39 / proposal-explicit-resource-management

ECMAScript Explicit Resource Management
https://arai-a.github.io/ecma262-compare/?pr=3000
BSD 3-Clause "New" or "Revised" License
758 stars 30 forks source link

Anonymous using? #228

Closed joeedh closed 4 months ago

joeedh commented 4 months ago

I was wondering why a syntax like using lock() is not in the proposal. I couldn't find much public discussion of it, but while perusing the standard I noticed this image

Is this the reason why anonymous using is not supported, to avoid ASI hacks? It would be really nice to have using object.lock() but I can see how adding yet more grammar hacks to the language might not be a good idea.

rbuckton commented 4 months ago

There are two scenarios for an "anonymous using":

For "using without introducing a binding", we have the Discard Binding proposal. This was originally part of this proposal but was cut prior to Stage 3 and is instead being pursued as a separate proposal. Supporting locking scenarios is a major motivation for this proposal since we have been discussing the introduction of Mutex in the Shared Structs proposal:

{
  // take exclusive lock over some shared data
  using void = new UniqueLock(mutex);
  doSomethingWithSharedData(sharedData);  
} // lock is released

For "using in an expression position", explicit syntax for that was rejected for the following reasons:

  1. using MemberExpression is ambiguous with CallExpression:
    const x = using (y);
    const x = using (y).z;
  2. To ensure scope effects from using and await using are explicitly discoverable at the start of a Statement:
    {
     using x = ...;
     await using y = ...;
    }

    vs.

    {
     const x = someArbitrarilyLongAndComplexExpression1(using ...);
     const y = someArbitrarilyLongAndComplexExpression2(await using ...);
    }

(2) is especially important for await using since it introduces an implicit await at the end of the block.

However, you can still achieve "using in an expression position" through the use of DisposableStack/AsyncDisposableStack, which still maintains the invariant in (2):

{
  await using stack = new AsyncDisposableStack();
  const x = someArbitrarilyLongAndComplexExpression1(stack.use(...));
  const y = someArbitrarilyLongAndComplexExpression2(stack.use(...));
}