Emmanuel Touzery
Emmanuel Touzery

Reputation: 9173

using the Maybe monad with monadic actions

When I have for instance a Maybe value, and I want to apply something on it when it's a Just or just keep a Nothing if it was a Nothing, I have many ways to achieve that in haskell. However I did not find a concise way when I want to apply on it a monadic action.

In this code, to get d I can just use fmap. But I can't use it to get c because that one requires a monadic (IO in this case) action to apply on the value in the Maybe.

import Control.Applicative

main = do
    let x = Just "test.txt"
    let d = reverse <$> x -- good
    print d
    c <- case x of -- verbose
     Nothing -> return Nothing
     Just y -> Just <$> readFile y
    print c

This is a way it can be done, but it's also too verbose.

    c' <- maybe (return Nothing) (\a -> Just <$> readFile a) x
    print c'

I'm sure there's a shorter way, I just can't see it right now...

Upvotes: 2

Views: 375

Answers (2)

Andr&#225;s Kov&#225;cs
Andr&#225;s Kov&#225;cs

Reputation: 30103

traverse does what you want here:

c' <- traverse readFile x
print c'

Upvotes: 3

CR Drost
CR Drost

Reputation: 9807

You're looking for traverse from Data.Traversable -- it's sort of a sequence/mapM construct applied to monads which are not the list monad (except it's applied to "traversables" and works for things which are weaker than "monads"). So for your particular case the function is just:

traverseMaybe f Nothing = return Nothing
traverseMaybe f (Just x) = fmap Just (f x)

but Data.Traversable defines a more general class:

class (Functor t, Foldable t) => Traversable t where
    {-# MINIMAL traverse | sequenceA #-}

    -- | Map each element of a structure to an action, evaluate these
    -- these actions from left to right, and collect the results.
    -- actions from left to right, and collect the results. For a
    -- version that ignores the results see 'Data.Foldable.traverse_'.
    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
    traverse f = sequenceA . fmap f

    -- | Evaluate each action in the structure from left to right, and
    -- and collect the results. For a version that ignores the results
    -- see 'Data.Foldable.sequenceA_'.
    sequenceA :: Applicative f => t (f a) -> f (t a)
    sequenceA = traverse id

    -- | Map each element of a structure to a monadic action, evaluate
    -- these actions from left to right, and collect the results. For
    -- a version that ignores the results see 'Data.Foldable.mapM_'.
    mapM :: Monad m => (a -> m b) -> t a -> m (t b)
    mapM = traverse

    -- | Evaluate each monadic action in the structure from left to
    -- right, and collect the results. For a version that ignores the
    -- results see 'Data.Foldable.sequence_'.
    sequence :: Monad m => t (m a) -> m (t a)
    sequence = sequenceA

This will also work for applicative functors that are not monads, like ZipLists.

Upvotes: 6

Related Questions