
On 12/7/06, apfelmus@quantentunnel.de
Cat Dancer wrote:
I have a program that performs a series of IO operations, each which can result in an error or a value. If a step returns a value I usually want to pass that value on to the next step, if I get an error I want to do some error handling but usually want to skip the remaining steps.
Thus I have a lot of functions with return types like IO (Either String x), where x might be (), Integer, or some other useful value type, and a lot of case statements like
You are on the right track. The point is that (IO (Either String a)) is a Monad, too. This allows you to write the ever repeating case statements once and forall:
newtype ErrorIO a = ErrorIO (IO (Either String a))
instance Monad ErrorIO where return x = return (Right x) f >>= g = do ex <- f case ex of e@(Left _) -> return e Right x -> g x
It happens that you can parametrize this on IO:
newtype ErrorT m a = ErrorT (m (Either String a)) type ErrorIO a = ErrorT IO a
instance Monad m => Monad (ErrorT m) where ... -- same as above
And you just rediscovered monad transformers.
I think I need to explain how thoroughly clueless I am :) I'm sure from a single example I could understand what was going on and elaborate from there. Let's say I want to get a line from the user, and either return an integer or an error string using ErrorT. import Control.Monad.Error import Control.Monad.Trans foo :: ?? foo = do -- something like this? a <- getLine if length a == 1 then return 123 else throwError "not a single character" main = do r <- ?? foo ?? print r -- prints Left "not a single character" or Right 123 ?