Some thoughts on the 'reveal' operator

Chip Morningstar chip@communities.com
Fri, 24 Sep 1999 12:28:14 -0700 (PDT)


MarcS sez:
>However, this also means that the value returned from a computation is
>always a capability at risk of being sent off into the far distance. In the
>absence of a reveal operation, in the presence of a computation that has no
>particular return value, E's default behavior (of returning whatever
>happened to be the result of the last operation) is a security breach. This
>seems inappropriate for a language with the goal of allowing secure
>computation.
>...
>It is really not acceptable for the default behavior of a capability
>language to be a security breach. Swaddle this problem in as many
>development standards and visual inspections and  coding conventions as you
>want, and it still just isn't acceptable. We need to save our security
>auditing strength for the real problems, not for default behavior accidents.

I am completely convinced by the rationale motivating the 'reveal'
operator. More specifically, I believe the principle that "reveal is an
explicit act, inaction leads to non-revelation" is superior to the converse
principle.

What I'm not convinced about is its syntactic realization in the language. If I
understand the problem correctly, the primary concern is with values escaping
from methods and functions, not so much with values escaping out of nested
blocks within a method or function into the enclosing block.

The difficulty with the current 'reveal' syntax is that if I *do* want to
reveal a value that is computed inside a nested block, I have to explicitly
reveal it at each level of nesting. This seems motivated by purely formal
concerns (i.e., ensuring that all blocks are semantically identical regardless
of context so we can readily define a lot of stuff in terms of Kernel-E) rather
than by considerations of the practical effect of this on the programmer (in
practice, the body of an 'if' *is* a qualitatively different beast from the
body of a 'define').

>From my perspective as a user of the language, either:

    define factorial(n) {
        ^if (n <= 1) {
            1
        } else {
            n * factorial(n-1)
        }
    }
or
    define factorial(n) {
        if (n <= 1) {
            ^1
        } else {
            ^n * factorial(n-1)
        }
    }

is superior to

    define factorial(n) {
        ^if (n <= 1) {
            ^1
        } else {
            ^n * factorial(n-1)
        }
    }

which gets even uglier in the case of more deeply nested constructs.  (Note
that in these examples I'm not taking a position about "^" vs. "reveal" or
about operator precedence; I'm just talking about block nesting.)

It looks to me like what we've got is the old function vs. procedure
dichotomy. If so, then wouldn't it be simpler to just declare things that way
like so many languages do, e.g.,

    define function factorial(n) {
        if (n <= 1) {
            1
        } else {
            n * factorial(n-1)
        }
    }
vs.
    define procedure dosomething(n) {
        if (n <= 1) {
            mythingie fargulate(n)
        } else {
            mythingie antifargulate(n)
        }
    }

Alternatively, one might just say "function foo" instead of "define function
foo", or maybe have some other marker syntax. I'm not arguing strongly for a
particular syntax here, just that it's icky (and to my mind unnecessary) to
require reveals in all nested blocks in order to get a value out.  The only
part of this that I'm not real sure of is the cases where a block is used in a
value context (e.g., passing a closure as a function parameter).