As an outsider, I notice that defer async {…}
is largely rejected as a requirement because it’s not necessary in the common case of minimal defer blocks.
But perhaps it should still be permitted, and, if declared, it could lead to easier scanning and helpful warnings and errors?
It’s akin to type-inference for a local variable. In complicated cases it can be clearer to state the intention up front, even if redundant, for purposes of scanning the code. The verbosity of prefix declarations can be easier to read than the more concise but indefinite inference from scanning the whole block, particularly for complex aspects.
A defer block is unlike do, while, or if because it has this weird non-local action-at-a-distance effect that’s already hard to think about. For async calls in particular, it would be helpful to know that there’s always another suspension point for the given context. (After all, it was decided to require await
at every possible suspension point.)
As a precedent, I’d think the same could be true for other effects: even when they could be inferred (somewhere in the nether regions of the closure…), they should be able to be stated for clarity and fo feedback when they’re not true (e.g., when other types of errors are being thrown).
But how far to take this prefix/declaration approach?
If/since async
is to be inferred, how would the user declare that they intend the defer block to be synchronous in an otherwise asynchronous function? Swift doesn’t have the inverse sync
declaration to state the negative (throws(Never)
is nice that way).
One workaround for that would be to declare a function local to the context and call that in the defer block (converting it to the minimal case). (I’ve always assumed without investigating that the compiler would avoid a runtime function call for local functions, but I should probably check that, esp. for defer.) The same workaround applies to the declared-async case. But the ceremony seems inapt for Swift; we don’t require variables to be declared with types first in one statement before they could be used in another below.
I find myself starting to use defer a lot, not just for resources and function exits. I do more than I should of complicated parallel struct/array loops where I put defer’s for progressing the data next to each access, and it would help to have the effects declared for each.