
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. Regards, apfelmus PS: In the special case of IO, you can also use exceptions. But using ErrorT is better style.