Sign In/My Account | View Cart  
advertisement


Listen Print

Apocalypse 6
by Larry Wall | Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16

Editor's Note: this Apocalypse is out of date and remains here for historic reasons. See Synopsis 06 for the latest information.

Appendix C: Hypotheticality and Flight Recorders

[This is a portion of a letter I sent to the design team. This stuff is still in discussion with the internals folks, so please take this as informative rather than definitive. But I just thought you might like to see how sausage is made. :-) --Larry]


    : Seems like you're going to have to explain the C<TEMP>/C<RESTORE> 
    : relationship in A6, Larry, since C<RESTORE> isn't even mentioned
    : there at present.

I'd like to explain it primarily by making both of them unnecessary most of the time.


    : But maybe after a good nights sleep, eh? ;-)

Okay, I've had a night's sleep. Whether it was a good one remains to be seen. But here's what I'm after. Forget implementation for a moment. What's the interface that people want, in the abstract? You were starting to think this way yourself with "suppose {...}". So let's do some supposin'.

I'll talk about caller and callee here, but I'm really talking about the user's abstract view vs. the user's implementation view, so it applies to variables, lvalue routines, and rvalue routines alike.

On the caller side, people want to be able to make a temporary assumption or a hypothesis. There is some scope over which the hypothesis is stated, and then some scope over which the hypothesis is assumed. At the end of that scope, the hypothesis may or may not be retracted. (I'm trying not to state this in terms of temp and let, just to keep our current ideas out of it.)

Historically, the scope of the hypothesis statement is a single variable/value, because local only knew how to temporize that kind of thing. The scope of the hypothesis assumption has always extended to the end of the current dynamic scope.

In the very abstract view, supposing is a transactional function with two arguments, the first one of which establishes a scope in which any state change is labelled as provisional. The second argument establishes a scope in which we work out the ramifications of that supposing, which may include other supposings. In classical terms, they're the protasis and apodosis:


    suppose { <pro> } { <apo> }

At the end of the second scope we decide whether to succeed or fail. On failure, we unsuppose everything that was supposed from the beginning, and upon success, we allow certain new "facts" to leak out into a larger reality (which may itself be a hypothesis, but leave that aside for the moment). It's basically commit/rollback.

It could also be written:


    suppose <pro> {
        <apo>
    }

to make it look more like an if. But up till now we've written it:


    {
        temp <pro>;
        <apo>
    }

which actually works out fine as a syntax, since every statement is in a sense conditional on preceding statements. If we want to allow a hypothetical result to leak out, we use "let" instead of "temp". Whatever. I'm not caring about the syntax yet, just the abstract interface.

And the abstract interface wants both <pro> and <apo> to be as general as possible. We already have a completely general <apo>, but we've severely restricted the <pro> so far to be (in Perl 5) a storage location, or in Perl 6 (Seb), anything with a .TEMP method. You'd like to be able to turn anything involving state changes into an <pro>, but we can't. We can only do it to values that cooperate.

So the real question is what does cooperation look like from the "callee" end of things? What's the best interface for cooperating? I submit that the best interface for that does not look like TEMP => {}, or RESTORE {}. It looks like nothing at all!


    sub foo { $x = 1234; }
    $x = 0;
    {
        temp foo();
        print $x;       # prints 1234
    }
    print $x;           # prints 0

How might this work in practice? If Perl (as a language) is aware of when it is making a state change, and if it also aware of when it is doing so in a hypothetical context (*any* hypothetical context in the dynamic scope), then Perl (as a language) can save its own record of that state change, filing it with the proper hypothetical context management authorities, to be undone (or committed) at the appropriate moment.

That's fine as long as we're running in Perl. Where an explicit TEMP method is useful is in the interface to foreign code or data that doesn't support dynamically scoped hypotheticality. If a Proxy is proxying for a Perl variable or attribute, however, then the STORE already knows its dynamic context, and handles temp and let implicitly just as any other Perl code running in hypothetical context would.

As for a hypothesis within a hypothesis, I think it just means that when you refrain from UNDOing the let state changes, you actually KEEP them into a higher undo list, if there is one. (In practice, this may mean there aren't separate LAST and UNDO lists. Just a LAST list, in which some entries do a KEEP or UNDO at the last moment. Otherwise a let within a let has to poke something onto both a keep list and an undo list. But maybe it comes out to the same thing.)

(In any event, we do probably need a name for the current innermost supposition we're in the dynamic scope of. I have my doubts that $?_ is that name, however. $0 is closer to it. Can thrash that out later.)

That's all very powerful. But here's where it borders on disruptive technology. I mentioned a while back the talk by Todd A. Proebsting on Disruptive Language Technologies. In it he projects which new disruptive language technologies will take over the world someday. The one that stuck in my head was the flight data recorder, where every state change for the last N instructions was recorded for analysis in case of failure. Sound familiar?

Taken together with my hypotheticality hypothesis, I think this likely indicates a two-birds-with-one-stone situation that we must design for. If state changes are automatically stored in a type-appropriate manner, we don't necessarily have to generate tons of artificial closures merely to create artificial lexical variables just so we have them around later at the right moment. I don't mind writing double closures for things like macros, where they're not in hot code. But let and friends need to be blazing fast if we're ever going to use Perl for logic programming, or even recursive descent parsing. And if we want a flight data recorder, it had better not hang on the outside of the airplane where it'll induce drag.

And that's what I think is wrong with our Sebastopolian formulation of .TEMP. Am I making any sense?

Larry