[E-Lang] pending revision of E in a Walnut

Mark Seaborn mrs35@cam.ac.uk
Sun, 26 Aug 2001 23:57:19 +0100

"Mark S. Miller" <markm@caplet.com> writes:

>     when (xVow) -> done(x) {
>         # x in scope
>         doSomething(x)
>     } catch prob {
>         # x not in scope.  prob in scope
>         handle(prob)
>     }

> Proposal: If doSomething(x) does throw a problem, catch this problem in the 
> catch clause as well -- binding prob and calling handler.  Since the "x" 
> parameter of "done(x)" can be any pattern, the catch clause can catch its 
> failure to match as well.

A problem I have with exception-handling constructs is that they tend
to be too liberal in where they catch exceptions from.  In OCaml, I
tend to use the idiom:

  match (try `Success (f())
         with exn -> `Failure exn) with
    | `Success x -> success x
    | `Failure exn -> failure exn

I could write

  try success (f())
  with exn -> failure exn

which would be more concise, but might result in `success' being
partially executed and then `failure' executed, which could be
harmful.  I really want to catch exceptions only from the expression
`f()', and I want only one of `success' or `failure' to be called.

So this change in the semantics of `when' wouldn't always be
desirable, since it makes it harder to do the sort of thing the first
fragment of OCaml does.

On a related topic, a common idiom in E seems to be to create
(promise, resolver) pairs in order to return the promise immediately
and resolve it in the `done' or `catch' clause of a `when' expression.
But since `when' currently doesn't return any interesting value
(correct me if I'm wrong on this), why not make `when' return a
promise for the value that is returned by the `done' or `catch' clause
(with the promise becoming broken if the clause executed throws an
exception)?  In other words, make

  when(X) -> done(x) { Y }
  catch e { Z }

give the behaviour currently given by

  def [promise, resolver] := PromiseMaker()
  when(X) -> done(x) {
    try { resolver resolve(Y) }
    catch e { resolver smash(e) }
  catch e {
    try { resolver resolve(Z) }
    catch e2 { resolver smash(e2) }

(Except that exceptions should not be caught when calling resolver's
smash method, only when evaluating Y or Z, but this is fiddly to do as
explained above.  Also the expansion of `when' could probably remove
the use of `try' by using eventual sends for turning control-flow
exceptions into data-flow exceptions.)

Using PromiseMaker explicitly creates the risk that a resolver will
never get called, which could cause datalock.  Extending the behaviour
of `when' gives a way of eliminating the use of PromiseMaker, reducing
that risk and making code shorter and clearer.

This extension provides a path for exceptions from the `done' and
`catch' clauses to flow along.  If no result flows out from the `when'
expression, odds are that those exceptions don't make any difference
and don't need to be reported.

         Mark Seaborn
   - mseaborn@bigfoot.com - http://www.srcf.ucam.org/~mrs35/ -

             Out, damned tagline! out, I say!