### Throwing

[
  Datatype throwing
  Funcon   thrown
  Funcon   finalise-throwing
  Funcon   throw
  Funcon   handle-thrown
  Funcon   handle-recursively
  Funcon   catch-else-throw
]


Meta-variables
  R, S, T, T', T'' <: values


Datatype
  throwing ::= thrown(_:values)
/*
  `thrown(V)` is a reason for abrupt termination.
*/


Funcon
  finalise-throwing(X:=>T) : =>T|null-type
   ~> finalise-abrupting(X)
/*
  `finalise-throwing(X)` handles abrupt termination of `X` due to
  executing `throw(V)`.
*/


Funcon
  throw(V:T) : =>empty-type
   ~> abrupt(thrown(V))
/*
  `throw(V)` abruptly terminates all enclosing computations uTil it is handled.
*/


Funcon
  handle-thrown(_:T'=>T, _:T''=>T) : T'=>T
/*
  `handle-thrown(X, Y)` first evaluates `X`. If `X` terminates normally with
  value `V`, then `V` is returned and `Y` is ignored. If `X` terminates abruptly
  with a thrown eTity having value `V`, then `Y` is executed with `V` as
  `given` value.
  
  `handle-thrown(X, Y)` is associative, with `throw(given)` as unit.
  `handle-thrown(X, else(Y, throw(given)))` ensures that if `Y` fails, the
  thrown value is re-thrown.
*/
Rule
                    X --abrupted( )-> X'
  --------------------------------------------------------
  handle-thrown(X, Y) --abrupted( )-> handle-thrown(X', Y)
Rule
    X --abrupted(thrown(V'':values))-> X'
  ----------------------------------------------
  handle-thrown(X, Y) --abrupted( )-> give(V'', Y)
Rule
                    X --abrupted(V':~throwing)-> X'
  ---------------------------------------------------------
  handle-thrown(X, Y) --abrupted(V')-> handle-thrown(X', Y)
Rule
  handle-thrown(V:T, Y) ~> V


Funcon
  handle-recursively(X:S=>T, Y:R=>T) : S=>T
    ~> handle-thrown(X, else(handle-recursively(Y, Y), throw(given)))
/*
  `handle-recursively(X, Y)` behaves similarly to `handle-thrown(X, Y)`, except
  that another copy of the handler attempts to handle any values thrown by `Y`.
  Thus, many thrown values may get handled by the same handler. 
*/


Funcon
  catch-else-throw(P:values, Y:=>T) : =>T
    ~> else(case-match(P, Y), throw(given))
 /*
   `handle-thrown(X, catch-else-throw(P, Y))` handles those values thrown by `X`
   that match pattern `P`.  Other thrown values are re-thrown.  
 */
