[{TableOfContents}]

!!!How should we handle exceptions in Orc??

I had mentioned last week that there are some Php exception cases that would prove difficult in Orc and promised some examples.  And while exceptions are by no means necessary to write correct programs, in my opinion they can simplify error handling significantly (though they're not without their own complications).  In general, all the exception cases I saw in Scalr, the management frontend for Amazon's cluster, are quite simple.

Scalr (like most programs) uses exceptions to handle cases where errors are propagated across function calls, cleanly separating the error handling code from return value etc.  Simple exception handling can be simulated in Orc, by having expression F simply calling err.write(..) to terminate the expression:

{{{
val c = Buffer()
val err = Cell()
repeat(c.get) <<
    F >x> c.put(x) >> stop
  | err.read() >> c.closenb()}}}

This should work fine for simple exception handling, but it requires both F and the exception handler both use the same err cell.  In theory one could pass in the name of the cell being waited on by the handler, but this would require all functions between the handler and the throw to pass the err cell.

Another problem arises in the case of nested exceptions.  In Php (like most languages) catch blocks can wait for typed exceptions: if the handler can't handle the thrown exception its propagated up the call stack.  This could again be simulated in Orc, but would require more work to explictly check types and possibly rethrow up the call stack.

!! Implementing exceptions with sites

I spent some time trying to find an implementation which used only sites (with access to the appropriate token state) rather than new DAG nodes. Unfortunately, such an implementation has a very hard time with several things:
# allowing handlers to catch more than one exception thrown from the body (the exception handler in the user guide, for example, can't do this)
# cleaning up handler state as soon as it is no longer needed
# cleaning up a throwing token as soon as it is no longer needed (even if the handler is still executing)
# routing publications from the handler to the right place
# executing the handler in its lexical environment
That's not to say it couldn't be done, just that it's very complicated and therefore probably a bad implementation strategy.

!!! A Modest Proposal

Here is an implementation sketch for a very basic exception mechanism similar to Javascript (and ML, Java, Python, etc). The only innovation is that exceptions in a thread can be caught by its parent. An example:

[{orc runnable='false'

(
    dangerousOperation1()
  | dangerousOperation2()
) catch(e) (
  logException(e) >>
  throw e
)
}]

''F'' {{ catch}}(''x'') ''G'' compiles to the DAG:
{{{
    pushEH
    /    \
   F   assign(x)
   |     |
 popEH   G
   \     /
    \   /
     \ /
     tau 
}}}

* pushEH's children: __body__ and __handler__. When a token ''t'' arrives at a pushEH:
** Fork ''t'' as ''t1'' and move ''t1'' to the handler.
** Push ''t1'' onto ''t''s handler stack.
** Put ''t'' into a new region which kills  ''t1'' when it closes.
** Move ''t'' to the body.
** Comment: this is very similar to the implementation of otherwise. The extra token is necessary to keep the region containing the handler open as long as it could still catch exceptions.
* popEH has one child: __body__. When a token ''t'' arrives at a popEH:
** Pop ''t''s region and handler stack.
** Move ''t'' to body.

{{throw }} ''F'' compiles to the DAG:
{{{
    F
    |
  throw
}}}

* throw has no children. When a token ''t'' arrives at throw:
** Let ''t1'' be the top of ''t''s handler stack.
** Fork ''t1'' as ''t2''.
** Set the value of ''t2'' to the value of ''t''.
** Activate ''t2''.
** Kill ''t''.
** Comment: ''t'' must not be killed until ''t2'' is activated to ensure that the region isn't killed prematurely.

I have glossed over the throwing of exceptions from sites. In the implementation of the {{call}} node, when the called site signals an error this should be handled as if the token visited a {{throw}} node carrying the error value.

!! Guarded Handlers

In most languages, exception handlers can declare a type or pattern which specifies the exceptions that handler is prepared to handle. This doesn't require any special support from the runtime. Instead, the handler can check whether it wants to handle the exception and if not rethrow it. We will probably want to provide syntactic sugar for pattern matching. Roughly,
[{orc runnable='false'

f catch(P(a,b)) g
}]

would be translated to
[{orc runnable='false'

f catch(x) (
  def h(P(a,b)) = g
  def h(c) = throw c
  h(x)
)
}]
where {{x}} and {{h}} are not free in {{g}}. In practice, the compiler would generate {{h}} inlined to avoid the unnecessary overhead of creating a closure.

!! Exceptions and otherwise

It's not obvious how exceptions should interact with the otherwise combinator; i.e. in {{(M() ; f) catch(e) g}}, if {{M()}} throws an exception should both {{f}} and {{g}} execute? Probably.

''Andrew says:''

I suppose if you didn't want {{f}} to run if {{M()}} throws, you could rewrite it as:
{{(M()) catch(e) g ; f}}

But the weirder case is maybe something like this:
{{{
def foo() = M();f
(foo()) catch(e) g
}}}
In which case you could be in the handler {{g}} but then {{foo}} is running?  Generally functions
either return or throw an exception, not both.  In a way this feels similar to the case where we have a
fork inside the {{try}} block and one of the processes throws an exception - do you kill the other thread?
It seems like this should be decided by the handler?

Exceptions with the pruning combinator - suppose we have: 
{{x <x< (foo() | bar()) catch (e) h}}

if {{foo()}} throws, it seems like might like {{bar()}} to have a chance to return (this might be a situation where you'd want h to decide whether to return or not...)

!! Exceptions and pruning

Another interesting case is:
{{{
let(f() | throw e) catch(e) e
}}}

Should the thrown exception halt f?  If so, then uncaught exceptions are no longer semantically equivalent to silent expressions, which is a drastic change from our current perspective on error handling.  If not, then exceptions provide a rather strange way to route publications.

!! Exceptions as an orthogonal publication channel

Something David suggested to me a while back is that you can think of         exceptions as publications along a different channel than regular publications (kind of like how shell scripting lets you redirect stdout and stderr differently).  So "throw e" is the exceptional analogue to "e" (publishing a value), and "catch(e)" is the exceptional analogue to ">e>" (handling a publication).  The regular   combinators are transparent to exceptions (they pass them through  unaffected), and the exception combinators are transparent to regular publications.  This explains why it must be the case that "throw e" == "stop" from the perspective of regular publications.

An arbitrary choice in the design is whether publications of an exception handler should come out as regular publications or exceptions; I chose regular publications so that there is a nice symmetry between "throw", which converts regular publications into exceptions, and "catch", which converts exceptions into regular publications.

I haven't proposed an exceptional analogue to "<e<" -- this would kill the RHS when the first exception is thrown.  Such a combinator isn't strictly necessary, but it would allow you to easily terminate a bunch of threads when any of them throws an exception, without requiring you to use channels or other contortions to route the regular publications.

What about the exceptional analogue to ";"?  This would run the RHS only if the LHS does not throw an exception.  So far I can't think of a good use for this.

!!! Todo:
* Could exceptions be implemented as syntactic sugar in Orc?  (I think so..)
* ML and Erlang both support exceptions, would this work in Orc, or are there deep reasons why not?
* Any thoughts on adding exceptions vs keeping orc small
* The relationship between errors and silent expressions
* Or is there a nice Orc abstraction (like the one above) that could handle the nested/typed cases well?

!!!Notes from Adrian:
* Exceptions - single threaded - not the right model??
* Erlang-style error propagation (notify parent)??
* Lisp Conditions??

!!! Examples of Php exceptions in Scalr:
Here are three examples of using exceptions in Scalr which are representative of




the way exceptions are used.  Note this is fairly simple, these examples should
be reasonable to implement in ORC:

* Exceptions thrown across function calls
* Rethrowing the exception up the stack
* A simple handler where the type of the exception is known (note only the base exception type is used) - The above Orc abstraction should be able to handle this easily.

{{{
class Scalr
{
    public static function AttachObserver ($observer, $isdeffered)
    {
        if ($isdeffered)
            $list = & self::$DeferredEventObservers;
        else
            $list = & self::$EventObservaers;
    
        if (array_search($observer, $list) !== false)
            throw new Exception(_('Observer already attached to class <Scalr>'));
        
        $list[] = $observer;
    }

    ...



    public static function FireDeferredEvent ($farmid, $event_type, $event_message)
    {
        try
        {
            // Notify class observers
            foreach (self::$DeferredEventObservers as $observer)
            {
                // Get observer config for farm
                $config = self::GetFarmNotificationsConfig($farmid, $observer);
                
                // If observer configured -> set config and fire event
                if ($config)
                {
                    $observer->SetConfig($config);
                    $res = call_user_func(array($observer, "On{$event_type}"), $event_message);
                }
            }
        }

        catch(Exception $e)
        {
            Logger::getLogger(__CLASS__)->fatal("Exception thrown in Scalr::FireEvent(): ".$e->getMessage());
        }
            
        return;
    }
    
    ...

    public static function FireEvent($farmid, Event $event)
    {
        try
        {
            // Notify class observers
            foreach (self::$EventObservers as $observer)
            {
                $observer->SetFarmID($farmid);                    
                Logger::getLogger(__CLASS__)->info(sprintf("Fire event: %s::%s", get_class($observer), "On{$event->GetName()}"));
                call_user_func(array($observer, "On{$event->GetName()}"), &$event);
            }
        }

        catch(Exception $e)
        {
            Logger::getLogger(__CLASS__)->fatal("Exception thrown in Scalr::FireEvent(): ".$e->getMessage());
            throw new Exception($e->getMessage());
        }
    }
    
    ...

}
}}}