[E-Lang] Thanksgiving present for E programmers
Mark S. Miller
markm@caplet.com
Wed, 22 Nov 2000 07:40:47 -0800
At 06:54 AM 11/22/00, Jonathan S. Shapiro wrote:
>Language lawyer warning:
>
>> E's :type syntax, as you clearly fully understand from the
>> rest of your message, expresses a check that must be performed at runtime.
>
>I'm sure this is an accurate statement of what is actually required, but it
>is a bad statement of specification. A (possibly) better statement of
>specification is:
>
> E's :type syntax expresses a predicate that must be true
> at runtime for execution to follow the normal flow of
> control. If the predicate is false, an exception is thrown (?).
> The predicate is normally verified by a runtime check,
> but the compiler is free to optimize this check away if
> it can determine statically that the check is unnecessary.
>
>I believe this is consistent with the rest of your note, but I think this
>verbiage makes a clearer statement of the requirement. If you don't like the
>word "predicate" for your readers, substitute "type requirement".
You're going in the right direction. A clear statement is somewhere between
yours and mine. The issue is that the check may be coded in E, and may
therefore have side effects or other time dependent behavior. While this
isn't recommended, the semantics specify a simple sequential run time model
where the check "happens" at a given time in the computation. It's exactly
like the issue of specifying the semantics of
if (foo()) { ...
in any C-like language. Depending on compiler cleverness, the actual order
in which things happen, or whether they can even be said to happen at all,
can be almost anything. However, since foo() may effects beyond evaluation
to a value (cause side effects, read state side effected by others, throw an
exception), C-like languages specify exactly when foo() must seem to have
been called.
How do instruction set designers phrase such things? The same issue arises,
and instruction set designers often know how to speak more precisely than do
most language designers, including myself.
Since foo() above is assumed to evaluate to a boolean, one might also call
it a "predicate", but, like the E :type check, it isn't something a
mathematician would recognize as a predicate. Which reminds me of another
very cool thing about E: Since E will have checkable confinement by auditing
(step #2 of http://www.erights.org/elib/capability/factory.html ), when the
programmer wishes to ensure that a "predicate" he's calling has no side
effects, and is therefore closer to the mathematician's "predicate", he can
first check that the predicate is confined:
def safeFoo :confined := foo
if (safeFoo()) { ...
If foo is not checkably confined, the attempt to define safeFoo will
throw an exception, and safeFoo will never come to be in scope. Similarly for
def safeType :confined := type
def x :safeType := y
Such code sequences may very well help out the compiler as well as the
programmer, as it provides more guarantees about what re-orderings cannot
change the semantics of the program.
>A proper specification, of course, must also specify the interaction between
>the predicate check and the inheritance design, if any. Accompanying
>explanatory notes should identify any hazards associated with this
>interaction -- I'm thinking here about a statement to the effect that
>inheritance frequently obscures contravariance bugs, and that depending on
>the satisfaction criteria the meaning of the type check differs.
All valid points, but fortunately not yet relevant for E. E has no
kernel-level inheritance, and the inheritance it does have
http://www.erights.org/elang/blocks/inheritance.html is just syntactic sugar
for a pattern of delegation. E has no separate declaration of a typed
interface that an implementation could be said to implement or not. OTOH, E
needs such a declaration, and the upcoming release will experimentally
introduce the beginnings of one, so these issues will probably eventually
become relevant.
Fortunately, the perspective presented by Giuseppe Castagna's amazing paper
"Covariance and Contravariance: Conflict Without a Cause"
ftp://ftp.ens.fr/pub/dmi/users/castagna/covariance.ps.Z (found by Eric
Drexler) suggests that, by starting with a runtime checking approach, we may
be closer to supporting the right solution than one would have thought.
However, at this point I'm going to beg off further exploration of this
topic. Static type theories make my brain ache, which is one of the reason
E moves the problem to (apparent) runtime and leaves it to other to build
static checkers outside the language.
>> By "static" checking, I've always meant rejection sometime before the
>> program is allowed to begin execution.
>
>Nit: I think you mean "...before the program is considered semantically
>meaningful." Or perhaps "...before the program is well-formed."
These others are good as well, but I meant what I said, which is the
operational issue. Were I to say, for example, "...before the program is
well-formed", I'd still need to say that a non-well-formed program may not
be allowed to start executing. I'm just cutting out the middle manpage.
Cheers,
--MarkM