lio- Labeled IO Information Flow Control Library

Safe HaskellTrustworthy



Exception routines much like the IO ones in Control.Exception (we duplicate the documentation below). There are two differences, however. First, LIO does not allow masking of asynchronous exceptions (since these are relied upon to kill a misbehaving thread). Hence, routines like onException are not guaranteed to run if a thread is unconditionally killed. Second, in a few cases (such as lWait) it is possible for the current label to be raised above the current clearance as an exception is thrown, in which case these functions do not catch the exception, either, since code cannot run under such circumstances.



class (Typeable e, Show e) => Exception e where

Any type that you wish to throw or catch as an exception must be an instance of the Exception class. The simplest case is a new exception type directly below the root:

 data MyException = ThisException | ThatException
     deriving (Show, Typeable)

 instance Exception MyException

The default method definitions in the Exception class do what we need in this case. You can now throw and catch ThisException and ThatException as exceptions:

*Main> throw ThisException `catch` \e -> putStrLn ("Caught " ++ show (e :: MyException))
Caught ThisException

In more complicated examples, you may wish to define a whole hierarchy of exceptions:

 -- Make the root exception type for all the exceptions in a compiler

 data SomeCompilerException = forall e . Exception e => SomeCompilerException e
     deriving Typeable

 instance Show SomeCompilerException where
     show (SomeCompilerException e) = show e

 instance Exception SomeCompilerException

 compilerExceptionToException :: Exception e => e -> SomeException
 compilerExceptionToException = toException . SomeCompilerException

 compilerExceptionFromException :: Exception e => SomeException -> Maybe e
 compilerExceptionFromException x = do
     SomeCompilerException a <- fromException x
     cast a

 -- Make a subhierarchy for exceptions in the frontend of the compiler

 data SomeFrontendException = forall e . Exception e => SomeFrontendException e
     deriving Typeable

 instance Show SomeFrontendException where
     show (SomeFrontendException e) = show e

 instance Exception SomeFrontendException where
     toException = compilerExceptionToException
     fromException = compilerExceptionFromException

 frontendExceptionToException :: Exception e => e -> SomeException
 frontendExceptionToException = toException . SomeFrontendException

 frontendExceptionFromException :: Exception e => SomeException -> Maybe e
 frontendExceptionFromException x = do
     SomeFrontendException a <- fromException x
     cast a

 -- Make an exception type for a particular frontend compiler exception

 data MismatchedParentheses = MismatchedParentheses
     deriving (Typeable, Show)

 instance Exception MismatchedParentheses where
     toException   = frontendExceptionToException
     fromException = frontendExceptionFromException

We can now catch a MismatchedParentheses exception as MismatchedParentheses, SomeFrontendException or SomeCompilerException, but not other types, e.g. IOException:

*Main> throw MismatchedParentheses catch e -> putStrLn ("Caught " ++ show (e :: MismatchedParentheses))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses catch e -> putStrLn ("Caught " ++ show (e :: SomeFrontendException))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses catch e -> putStrLn ("Caught " ++ show (e :: SomeCompilerException))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses catch e -> putStrLn ("Caught " ++ show (e :: IOException))
*** Exception: MismatchedParentheses

data SomeException where

The SomeException type is the root of the exception type hierarchy. When an exception of type e is thrown, behind the scenes it is encapsulated in a SomeException.


SomeException :: Exception e => e -> SomeException 

throwLIO :: Exception e => e -> LIO l aSource

Throw an exception.

catch :: (Label l, Exception e) => LIO l a -> (e -> LIO l a) -> LIO l aSource

A simple wrapper around IO catch. The only subtlety is that code is not allowed to run unless the current label can flow to the current clearance. Hence, if the label exceeds the clearance, the exception is not caught. (Only a few conditions such as lWait or raising the clearance within scopeClearance can lead to the label exceeding the clarance, and an exception is always thrown at the time this happens.)

handle :: (Label l, Exception e) => (e -> LIO l a) -> LIO l a -> LIO l aSource

A version of catch with the arguments swapped around.

try :: (Label l, Exception a1) => LIO l a -> LIO l (Either a1 a)Source

Similar to catch, but returns an Either result which is (Right a) if no exception of type e was raised, or (Left ex) if an exception of type e was raised and its value is ex. If any other type of exception is raised than it will be propogated up to the next enclosing exception handler.

onException :: Label l => LIO l a -> LIO l b -> LIO l aSource

Like finally, but only performs the final action if there was an exception raised by the computation.

finally :: Label l => LIO l a -> LIO l b -> LIO l aSource

A variant of bracket where the return value from the first computation is not required.



:: Label l 
=> LIO l a

Computation to run first

-> (a -> LIO l c)

Computation to run last

-> (a -> LIO l b)

Computation to run in-between

-> LIO l b 

When you want to acquire a resource, do some work with it, and then release the resource, it is a good idea to use bracket, because bracket will install the necessary exception handler to release the resource in the event that an exception is raised during the computation. If an exception is raised, then bracket will re-raise the exception (after performing the release).

evaluate :: a -> LIO l aSource

Forces its argument to be evaluated to weak head normal form when the resultant LIO action is executed.