[Haskell] Better Exception Handling

Scott Turner p.turner at computer.org
Wed Nov 24 11:21:58 EST 2004


On 2004 November 23 Tuesday 10:51, John Goerzen wrote:
> > for pure functions, returning Either Error Result is the way to go.

> One example: I've written an FTP client library.  For every operation,
> there are several possible outcomes... mainly: success, low-level
> network error, or server error.
>
> Having to pick apart an Either from every command to CD, set transfer
> types, etc. would get so tedious that the code would, I think, become
> spaghetti fast.

The way to deal with those kinds of details is to use Either in a monad.  I'm 
skeptical of the need for dynamic scope in conventional exception handling, 
so I took a shot at this problem, with satisfying results.  (I'm not keen on 
dynamic typing for that matter, but don't know of a nice way to avoid it.)  
Excerpts are below.  The full sample code is at 
http://pkturner.org/exception.tar

The Haskell Main module using the FTP library is comparable to your Python 
example.  

main = runException $ do
        ftp("ftp.kernel.org")
        (   cwd "pub/linux/kernel/v2.4"
            `except` (\(ErrorPerm e) -> lift $ do
                putStrLn ("caught temp error in cwd: " ++ e)
                exitWith (ExitFailure 2))
         )
        retrbinary "RETR ChangeLog-2.4.13" 
                (\block -> write block)
        quit
    `except` (\(ErrorPerm e) -> lift $ do
        putStrLn ("Permissions error " ++ e)
        exitWith (ExitFailure 2))
    `except` (\(ErrorTemp e) -> lift $ do
        putStrLn ("Temporary error, please try again later" ++ e)
        exitWith (ExitFailure 1))
    `except` (\(ErrorFTP e) -> lift $ do
        putStrLn ("Other FTP error" ++ e)
        exitWith (ExitFailure 2))
    `except` (\(ErrorAll e) -> lift $ do
        putStrLn ("Non-FTP error" ++ e)
        exitWith (ExitFailure 3))

Also, the FTP module demonstrates that it's straightforward to add new classes 
of errors in the exception hierarchy.

data ErrorPerm = ErrorPerm String
        deriving (Typeable)
instance Hierarchical ErrorPerm where
        parent (ErrorPerm msg) = Parent $ ErrorFTP msg
data ErrorTemp = ErrorTemp String
        deriving (Typeable)
instance Hierarchical ErrorTemp where
        parent (ErrorTemp msg) = Parent $ ErrorFTP msg
data ErrorFTP = ErrorFTP String
        deriving (Typeable)
instance Hierarchical ErrorFTP where
        parent (ErrorFTP msg) = Parent $ ErrorAll msg

type FTP = Exception IO

ftp :: String -> FTP ()
ftp str = if take 3 str == "ftp"
        then lift (return ())
        else raise $ ErrorPerm ("ftp " ++ str)




More information about the Haskell mailing list