Resource management (eg. try-with-resources)
On Fri, Nov 10, 2017 at 3:00 PM, Michał Wadas <michalwadas at gmail.com> wrote:
After I started writing code extensively using async-await I have noticed that I would like to have automated way of dealing with resource management.
This is something I'd really like to see as well.
What's the motivation for having an open
? Java's AutoCloseable
just has
close
; one would normally be acquiring these from a constructor or
function, which is effectively the open
...?
Can you give a concrete example using the syntax you're looking at? E.g.,
were you really thinking of actually having with
and as
? If so, I'd
rather steal more directly from Java (perhaps replacing their ;
with ,
):
try (a = giveMeAResource(), b = giveMeAnotherResource()) {
// `a` and `b` are in scope here
} catch (e) {
// ...`a` and `b` are not in scope here
} finally {
// ...`a` and `b` are not in scope here
}
...where (off-the-cuff):
- Both
catch
andfinally
are optional a
andb
are constants scoped to thetry
block (no need for the keyword, but could require or allow it for clarity)- Trailing commas on the resource declaration/initializer list are fine
b[Symbol.close]
is called first, thena[Symbol.close]
- Both
close
s are called prior to the execution of code in thecatch
orfinally
(if any) - If the
try
block throws, that error is the primary error and anyclose
calls that also throw are "suppressed errors" (more below) - If the
try
block doesn't throw, but anyclose
calls do, the firstclose
that fails becomes the primary error; any others that fail become suppressed errors
Suppressed error handling:
- If the primary error is an
Error
object, suppressed errors are added to asuppressed
array property on it (creating it if necessary) - If the primary error is not an
Error
object, they're lost other than being treated as unhandled errors by the environment (for instance: logged, perhaps causing script termination; thought required on that)
-- T.J. Crowder
Here's a C++-inspired alternative:
let res = value[Symbol.resource]()
- Get an optional promise to a resource. lf missing,res
is set tovalue
instead.res[Symbol.close]()
- Close a resource, optionally returning a promise.try let res = value
- Get the resource by invokingvalue[Symbol.resource]()
(or usingvalue
if the method is missing), assign it tores
, and invokeres[Symbol.resource]()
once it leaves the scope, even on error.try await let res = value
- Same as above, but awaitvalue[Symbol.resource]()
(if necessary) andres[Symbol.close]()
after calling them.try with expr
,try await with expr
- Same as above, but without declaring a binding. (Works well with transactions, locks, etc.)- Close exceptions replace thrown exceptions.
Benefits:
- Less nesting, more concise.
- Java's
try
-with-resources is clunky and awkward. Python'swith
statement is no different. - It works well with sync and async resources.
- User-level primitives are easier to write.
Of course, in the spec, there's a few logical cases where you'd want these methods to be defined/used:
%IteratorPrototype%[@@close]()
- returnthis.close()
if such a method exists.%AsyncIteratorPrototype%[@@close]()
- Similar to above.Atomics.lock(sab, index, size)
- Return a resource that acquires a lock for this section, and releases on@@close
.
After I started writing code extensively using async-await I have noticed that I would like to have automated way of dealing with resource management.
Scope-level manage was proven (Python, C++, Java at least) to be intuitive and error proof.
Previous discussion on this topic: esdiscuss. org/topic/resource-management
Example syntax:
try with Expression as Identifier {
} catch... finally...
Symbol.open and Symbol.close methods would be called respectively on entering and leaving try block.
Plays well with both synchronous and asynchronous resource access.
Another option is to reuse with:
with(expression as identifier) {
}
Though it would be confusing to statement with wildly different semantics in strict mode.