Stale. Taken from here,
but needs rewrite.
The Changing of the Guard
In the thread rooted here,
Terry Stanley proposes a set of naming conventions for naming variables,
functions, and objects. These conventions have evolved some since then
(eg, "Pass" has become "Rcvr"),
but we have essentially been following the practices recommended there,
with mixed results.
There has been a live controversy raging privately without resolution
that we should now make public. Should we be encoding near (supports immediate
calls) vs vow (successful resolution supports immediate calls)
vs rcvr (just do eventual sends) in the names (as this thread recommends,
and as we've been mostly doing since then), or should we instead, or in
addition, be using our guard annotations to express these distinctions;
as Terry now advocates (thanks!).
Whatever the resolution of this controversy, it seems like a good idea
to be able to express these distinctions with guard annotations. This
would be especially true if these guards could enforce the property they
state, as with other guards. Unfortunately, for the new ":vow"
and ":rcvr" guards, we can't enforce these properties at a reasonable
cost until we have good support for PassByCopy, so these guards are just
advisory for now. Nevertheless, as a way to capture programmer intent
in a machine understandable form, this seems worth doing anyway. The relevant
:near - (No change in meaning or implementation.) A near
reference passes this guard. All others get rejected. A near reference
supports immediate calls on the object it designates. This guard is
:pbc - (No change in meaning or implementation.) A near
reference to a PassByConstruction object. All PassByCopy objects are
also PassByConstruction. When a pbc is passed as an argument, the
value as received by the callee will be pbc. An unresolved reference
to a pbc will successfully resolve only to a pbc, never to a far reference.
This guard is properly enforced.
:vow and :vow[valueGuard] - (New as of 0.8.14.)
A vow may itself be a near, unresolved, or broken reference. If it's
currently unresolved, then its fulfillment (its successful resolution)
must be near. In other words, its resolution must be near or broken,
but never far. A possibly unresolved reference to a pbc is a vow.
A ":vow[valueGuard]" is a vow for something that
will pass the argument valueGuard. For example, a ":vow[int]"
is a vow for an int.
This guard is currently not enforced -- it is currently operationally
equivalent to ":any" and is used purely for documentation
purposes. Once it is enforced, it will send its argument valueGuard
to the specimen's host to coerce the specimen there. If the specimen
is remote, the valueGuard itself will have to be a pbc object.
The ":vow[valueGuard]" will then return a promise
for the result of (remotely) coercing the specimen by the valueGuard.
This preserves pipelining (which a local check would lose), and prevents
any messages from being delivered to a specimen that doesn't pass
the valueGuard, but relies of the specimen's host to run
the valueGuard honestly. If you want to trade pipelining
for local enforcement, this is easy to write in the language, but
seemed like the wrong default.
:rcvr and :rcvr[valueGuard] - (New as of 0.8.14.)
A rcvr is a reference that may be eventual, and whose resolution may
be far. Therefore, one should only deal with rcvrs using eventual
sends -- never by immediate calls. Without an argument guard, ":rcvr"
has the same operational meaning as ":any", and so is properly
if vacuously enforced. ":rcvr[valueGuard]" is a
reference for an object that will pass the argument valueGuard.
As with ":vow[valueGuard]", this is currently unenforced,
but will be enforced using the same technique documented above.
:any - (No change in meaning or implementation.) The specimen
can be any kind of reference, and no coercion is performed (ie, coerce
returns its argument). This guard is properly if vacuously enforced.
Though there is no operational difference between this and ":rcvr",
there is a documentation difference, and a possible difference in
static checking rules (see below). Whereas ":rcvr"
states that the declarer has special knowledge that he's warning his
client of, ":any" is better used when the client
may have better knowledge than the declarer. For example, a get/1
from a ConstList would be declared as ":any"
rather than ":rcvr", since the declarer know that
he's only handing back what was put in, which could be anything; whereas
the client knows what he put in.
These annotations seem adequate for a lint-like static checking advisor.
Here are some example rules that seem easy to automate. For concreteness,
"int" is used as an example of a pbc type, and a guard
for such a type that does no coercion (either passes its argument through
or complains), and "Counter" is used as an example of a PassByProxy
type and corresponding non-coercing guard. In the following "<="
means "subtype" and "=>" means "produces"
or "is of type". Note: I am not a type theory kind of guy, so
advice and corrections would be especially welcome.
- near <= vow <= rcvr
- int <= vow[int]<= rcvr[int]
- warn that rcvr[int] should be vow[int]
- Counter <= vow[Counter]<= rcvr[Counter]
- warn that vow[Counter] may want to be rcvr[Counter], though vow[Counter]
can be correct if you know that the counter is necessarily local.
- Given def f1():int, f1() => int, f1 <- () => vow[int]
- Given def f2():vow, f2() => vow, f2 <- () => vow (since f2
is a near reference to such a function)
- Given f2Rcvr :rcvr[F2], f2Rcvr <- () => rcvr (since f2Rcvr may
be remote, and a vow to it may be a rcvr to us.)
- Given f1Rcvr :rcvr[F1], f1Rcvr <- () => vow[int] (since a remote
reference to a vow for a pbc is still a vow for a pbc.)
- when (vow) -> done(near) is ok
- when (vow[int]) -> done(int) is ok
- when (rcvr) -> done(rcvr) is ok
- Given def f3(int), f3(int) is ok, f3 <- (int) is ok
- Given def f4(counter), f4(counter) is ok, f4 <- (counter) is ok
(since both f3 and counter are near references.)
- Given f4Rcvr :rcvr[F4], f4Rcvr <- (counter) is bad (since f4Rcvr
may be remote.)
- Given counter :Counter, warn of f4Rcvr <- (counter), since it's
probably bad, but is sometimes necessary and appropriate if the programmer
has special knowledge these are both resolved references into the same
remote vat. I doubt we should try to capture such special knowledge
in static checking rules.
This list is intended to be more suggestive than correct or complete,
in the hopes that someone who has competency with such matters may take
it from here. Please let me know if you're interested, or start an argument
about these rules on the e-lang list.