Search code examples
haskellmonadsoption-typeio-monad

Refactoring “staircasing” with case of `Maybe` values in `IO` code


The following function f attempts to read an Int twice by using an IO (Maybe Int) function twice, but “short-circuits” execution after successfully reading one Int:

readInt :: IO (Maybe Int)

f :: IO (Maybe Int)
f = do
  n1 <- readInt
  case n1 of
      Just n' -> return (Just n')
      Nothing -> do
        n2 <- readInt
        case n2 of
              Just n' -> return (Just n')
              Nothing -> return Nothing

Is there a good way to refactor this code? This would get very hairy if I extended it to three attempts…

(My thought process: Seeing this “staircasing” tells me that maybe I should be using the Monad instance of Maybe, but since this is already in the IO monad, I would then have to use MaybeT(?). However, I only need one of the readInt to succeed, so the Maybe monad's behaviour of erroring out on the first Nothing would be wrong here...)


Solution

  • You can use MaybeT and the MonadPlus instance to use msum:

    f :: MaybeT IO Int
    f = msum [readInt, readInt, readInt]