Subject: | Consider the semantics of `local` inside `await` |
Without the async/await syntax or similar behaviours, the semantics of `local` declarations in are clearly defined. `local` establishes a region of modification of a variable that is temporary in both dynamic and temporal scope, because without deferred continuations, those are the same thing.
However, once we have the ability to suspend and resume a function later on, we run into a mismatch of semantics due to the fact that a single dynamic scope can now be split across multiple disjoint temporal regions. To take a simple example:
our $DEBUG;
sub act { say STDERR "I'm now acting" if $DEBUG }
async sub foo
{
local $DEBUG = 1;
act();
await $f;
}
Semantically, it should be pretty clear here that the call to `act()` is intended to cause a debugging print to occur. The dynamic extent of the call to `act()` occurs within the dynamic extent of the `local $DEBUG` assignment, and that matches the temporal extent, so all is happy.
Next, consider:
async sub bar
{
local $DEBUG = 1;
await $f;
act();
}
Here, if the `await` expression has to suspend and resume on `$f` then the dynamic and temporal extents are now different. If we decide that `local` should have dynamic (rather than temporal) extent, then we can arrange for its value to be saved and restored around the `await` operation, effectively performing:
async sub bar
{
local $DEBUG = 1;
# save $DEBUG here
await $f;
# restore $DEBUG here
act();
}
This technique however does not extend beyond `await` expressions in other functions called by this one. Consider now:
async sub inner
{
act(); #1
await $f;
act(); #2
}
async sub baz
{
local $DEBUG = 1;
await inner();
act(); #3
}
It is clear here that the nested call to `act()` labelled #1 will see the intended value of `$DEBUG` because its temporal vs. dynamic extents have not become disconnected due to an intervening `await` expression. If we perform the localisation save/restore logic as before, then the call labelled #3 will also see it. However, the call labelled #2 will not, because it is nested inside a dynamic scope whose `await` expression did not realise to save and restore the value of `$DEBUG`, as it wasn't in the savestack for the scope of its own function.
--
Paul Evans