Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Common pattern to deal with Failure inside IO :: IO (Either String Int) [duplicate]

Tags:

haskell

monads

Trying to understand the pattern used to deal with possible failures inside IO. If its just one cases like below, its probably acceptable, but if the nesting goes on for a bunch of nested IO (Either String Int)s is there a common pattern to handle such types. For instance, if b in functionDoSomething is again a (Either a b) and fetching value on success and doing something with it again will be another such case. Is there a higher order function that I can use? I'm not comfortable with monad transformers yet, not sure if they can be used to deal with this specific monad stack. If they can be used here, is there a way to do it without using them.

import Control.Monad

functionCreate :: Int -> IO (Either String Int)
functionDoSomething :: Int -> IO b

functionUse :: IO ()
functionUse = do
   created <- functionCreate 10
   case created of
      (Right v)        -> void $ functionDoSomething v
      _                -> return ()
like image 507
queue Avatar asked Mar 05 '23 13:03

queue


1 Answers

I understand that you're new to Haskell, and that monad transformers aren't the first concept you'd want to grapple with. In a situation like this, it is, however, the pattern to use.

Monads in general enable you to 'weave in and out of functors', so to speak. Had you only had Either, you could have used the Either values with do notation to pull the Right values out of the values, while short-circuiting the Left cases.

In this case, however, you have a 'stack' of monads: Either inside of IO. Thus, when you attempt to use do notation, you're in the IO context, and that means that, as the OP illustrates, the values that you 'pull out of' the functions, using the <- arrow, are still Either values.

Monad transformers enable you to treat stacks of monads (like, in this case Either values inside of IO) as Monad instances, so that you can, e.g., use do notation on the stack.

While you'd expect the Either monad transformer to be called EitherT, it is, for various reasons, called ExceptT. You could simplify the OP code slightly like this:

import Control.Monad (void)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except

functionUse :: IO (Either String ())
functionUse = runExceptT $ do
  created <- ExceptT $ functionCreate 10
  liftIO $ void $ functionDoSomething created

Here, created is an Int value, which can then be passed to functionDoSomething.

like image 112
Mark Seemann Avatar answered Apr 25 '23 11:04

Mark Seemann