Search code examples
haskellmonad-transformersoption-typeio-monad

Monad transformers with IO and Maybe


I am trying to stack up IO and Maybe monads but either I don't understand monad transformers well enough or this is not possible using transformers. Can some one help me understand this?

f :: String -> Maybe String

main :: IO ()
main = do
  input <- getLine            -- IO String
  output <- f input           -- Maybe String (Can't extract because it is IO do block)
  writeFile "out.txt" output  -- gives error because writeFile expects output :: String

In the above simplified example, I have a function f that returns a Maybe String and I would like to have a neat way of extracting this in the IO do block. I tried

f :: String -> MaybeT IO String

main :: IO ()
main = do
  input <- getLine              -- IO String
  output <- runMaybeT (f input) -- Extracts output :: Maybe String instead of String
  writeFile "out.txt" output    -- gives error because writeFile expects output :: String

which lets me extract the Maybe String out in the second line of do block but I need to extract the string out of that. Is there a way to do this without using case?


Solution

  • Let's stick for a moment with your first snippet. If f input is a Maybe String, and you want to pass its result to writeFile "out.txt", which takes a String, you need to deal with the possibility of f input being Nothing. You don't have to literally use a case-statement. For instance:

    • maybe from the Prelude is case analysis packaged as a function;

    • fromMaybe from Data.Maybe lets you easily supply a default value, if that makes sense for your use case;

    • traverse_ and for_ from Data.Foldable could be used to silently ignore Nothing-ness:

      for_ (f input) (writeFile "out.txt") -- Does nothing if `f input` is `Nothing`.
      

    Still, no matter what you choose to do, it will involve handling Nothing somehow.

    As for MaybeT, you don't really want monad transformers here. MaybeT IO is for when you want something like a Maybe computation but in which you can also include IO computations. If f :: String -> Maybe String already does what you want, you don't need to add an underlying IO layer to it.