Reputation: 9173
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
Reputation: 30103
traverse
does what you want here:
c' <- traverse readFile x
print c'
Upvotes: 3
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