Open GoogleCodeExporter opened 8 years ago
Pro: conceptually, we could see this as “the current actor mirror” being
part of the context of a closure, so it
should be ‘captured’ by closures as well.
Con: most closures have no need for capturing the ‘current actor protocol’
because they do not depend on it.
Only a small minority depends on it (usually closures that serve as event
handlers, identified by the when:*
constructs)
Note: there is also a relationship with the dynamic variables library (see
at/lang/dynvars.at), where the
problem you mention can be seen more apparently. A dynvar can be assigned a
value only within the context
of a given block of code:
def x := dynVar: 0;
with: x is: 1 do: {
// in this scope, x = 1
}
However, this simple abstraction fails to scale to our asynchronous execution
model:
with: x is: 1 do: {
// in this scope, x = 1
when: someObj<-someMsg becomes: {
// in the callback, x is no longer 1, as it is not executed in the dynamic extent of the original block closure
application
}
}
The current work-around in the dynvars library is the following: if the block
of code passed to with:is:do:
returns a future, then the re-setting of the dynvar to its previous value is
postponed until that future has been
resolved. The above piece of code will then work, since when:becomes: returns a
future that is only resolved
once its callback closure has been invoked. Within the context of the callback
closure, x will be 1.
However, this still gives problems when there are multiple with:is:do: calls
for the same dynvar in the
program. The problem is this: with:is:do: relies on a synchronous
call-stack-behaviour to set and re-set the
dynvar. However, in an async event loop model, the synchronous call-stack
behaviour does not necessarily
hold. So, if calls return futures, these futures may be resolved "out of order"
(i.e. not obeying the LIFO
principle)
Original comment by tvcut...@gmail.com
on 16 Sep 2008 at 1:55
We could enforce that closures always capture the dynamic environment (i.e. the
bindings of all dynamic
variables at the moment of their creation) and reinstate them upon invocation.
The current actor mirror can
be seen conceptually as one such dynamic variable.
This design decision does seem to introduce a new set of problems as well.
First of all, we need to decide
whether method definitions also capture the said dynamic environment or not.
Second, a mechanism is
needed to resolve conflicts, when a dynamic variable is given a value in a
dynamic scope and a closure (or
method) is invoked which associated a different value with the same variable.
Using either value may result in
counterintuitive results.
None of these problems arise in the exemplar case, which uses when:*
constructs. The underlying reason is
that there, closures are used which will be invoked asynchronously, hence there
is no surrounding scope
which can introduce bindings for a dynamic variable (other than the default
actor mirror).
A conservative solution could therefore be to provide a callback: construct
which takes a closure as argument,
and wraps it to reinstate the bindings for all dynamic variables (including the
actor mirror) upon invocation.
Since the closure is wrapped, this mechanism does not interfere with regular
application of the closure
(required in case a non-literal closure is supplied).
As a downside, using a callback: construct does require the implementors of
when:* constructs to remember
to use this idiom explicitly, and requires (minor) changes to all existing
when:* construct implementations.
Original comment by smost...@gmail.com
on 17 Sep 2008 at 8:13
Original issue reported on code.google.com by
tvcut...@gmail.com
on 16 Sep 2008 at 1:47