Question

I'm trying to grasp how Haskell programs can avoid testing for "empty value". I am struggling to get rid of case expression in this program:

main =  do url:outputPath:[] <- getArgs
         let maybeUri = parseURI url
         case maybeUri of
              Just uri -> download uri outputPath
              Nothing -> return ()

My very rough understanding is that I should use monad transformer so that I can use single mappend on Maybe value within IO monad and 'do' syntax should be able to support it. How can I achieve this?

Was it helpful?

Solution

Use forM_ from Data.Foldable, which has the following type:

forM_ :: (Monad m, Foldable t) => t a -> (a -> m b) -> m ()

Maybe implements the Foldable class, behaving like a list with zero or one elements, so when you specialize the t in the above type signature to Maybe you get:

forM_ :: (Monad m) => Maybe a -> (a -> m b) -> m ()

You use it like this:

forM_ maybeUri $ \uri -> download uri outputPath

It will only run the action if the Maybe value turns out to be a Just.

OTHER TIPS

You could use Data.Maybe.maybe (it's also in the Prelude so no import needed):

main = do
  url:outputPath:[] <- getArgs
  let maybeUri = parseURI url
  maybe (return ()) (\uri -> download uri outputPath) maybeUri
  -- The above can also be written as:
  -- maybe (return ()) ((flip download) outputPath) maybeUri

maybe takes:

  • a function to run in case of a Nothing: here, (return ())
  • a function to run on the value inside a Just: here, (\uri -> download uri outputPath)
  • the Maybe value: maybeUri

I show an alternative way to express the function on the Just value, using a partially-applied function.

I would argue for using maybe because it makes it explicit, without having to write out the case expression, that you're dealing with a Maybe.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top