Asynchrony is actually a special case of a more general challenge
to static typing: non-determinism.
To illustrate this challenge, let us try an analogy. Dynamic binding allows us to get rid of explicit control structures when it comes to select proper processing, based on the actual type of items, e.g. in a collection. In contrast, we still have to bear with similar explicit control structures as we test the return values of functions:
void foo(Animal& a) {
switch (a.legs()) {
case 0: //...
case 2: //...
case 4: //...
case 6: //...
//...
}
}
We have, however, already seen in an example (in 2.2.2.b), that
operations may help to define a static scope for handling dynamically
determined behaviours. The example was the definition of separate
callback entry points in the initiator of an operation to process the
different kinds of possible results.
We can now apply this technique to the issue left open in the
introduction of 2.2.: how to avoid the existence of unbound references
for the handles providing location transparency. References can become
dangling after having first been bound. Preventing this is a matter of
reference counting on one hand, and of monitoring communication losses
on the other. Neither problem is trivial, but both are somehow
classical. The less classical issue is how to statically avoid the
construction of an unbound reference. This is where operations may be
used: acquiring a handle to a potentially remote resource, possibly
creating it, is an asynchronous task. The handle can only be
constructed in case of success, which can now be expressed as a
separate syntactic scope.