Best practice how to evaluate a list of Maybes

i am looking for a function which takes a function (a -> a -> a) and a list of [Maybe a] and returns Maybe a. Hoogle gave me nothing useful. This looks like a pretty common pattern, so i am asking if there is a best practice for this case?

>>> f (+) [Just 3, Just 3]
Just 6
>>> f (+) [Just 3, Just 3, Nothing]
Nothing

Thanks in advance, Chris

Answers


You should first turn the [Maybe a] into a Maybe [a] with all the Just elements (yielding Nothing if any of them are Nothing). This can be done using sequence, using Maybe's Monad instance:

GHCi> sequence [Just 1, Just 2]
Just [1,2]
GHCi> sequence [Just 1, Just 2, Nothing]
Nothing

The definition of sequence is equivalent to the following:

sequence [] = return []
sequence (m:ms) = do
  x <- m
  xs <- sequence ms
  return (x:xs)

So we can expand the latter example as:

do x <- Just 1
   xs <- do
       y <- Just 2
       ys <- do
           z <- Nothing
           zs <- return []
           return (z:zs)
       return (y:ys)
   return (x:xs)

Using the do-notation expression of the monad laws, we can rewrite this as follows:

do x <- Just 1
   y <- Just 2
   z <- Nothing
   return [x, y, z]

If you know how the Maybe monad works, you should now understand how sequence works to achieve the desired behaviour. :)

You can then compose this with foldr using (<$>) (from Control.Applicative; equivalently, fmap or liftM) to fold your binary function over the list:

GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2]
Just 3

Of course, you can use any fold you want, such as foldr, foldl1 etc.

As an extra, if you want the result to be Nothing when the list is empty, and thus be able to omit the zero value of the fold without worrying about errors on empty lists, then you can use this fold function:

mfoldl1' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a
mfoldl1' _ [] = mzero
mfoldl1' f (x:xs) = return $ foldl' f x xs

and similarly for foldr, foldl, etc. You'll need to import Control.Monad for this.

However, this has to be used slightly differently:

GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2]
Just 3

or

GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+)
Just 3

This is because, unlike the other folds, the result type looks like m a instead of a; it's a bind rather than a map.


As I understand it, you want to get the sum of a bunch of maybes or Nothing if any of them are Nothing. This is actually pretty simple:

maybeSum = foldl1 (liftM2 (+))

You can generalize this to something like:

f :: Monad m => (a -> a -> a) -> [m a] -> m a
f = foldl1 . liftM2

When used with the Maybe monad, f works exactly the way you want.

If you care about empty lists, you can use this version:

f :: MonadPlus m => (a -> a -> a) -> [m a] -> m a
f _ []      = mzero
f fn (x:xs) = foldl (liftM2 fn) x xs

What about something as simple as:

λ Prelude > fmap sum . sequence $ [Just 1, Just 2]
Just 3
λ Prelude > fmap sum . sequence $ [Just 1, Just 2, Nothing]
Nothing

Or, by using (+):

λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2]
Just 3
λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2, Nothing]
Nothing

So, maybeSum = fmap sum . sequence.


Need Your Help

JPA stops working after redeploy in glassfish

java jpa glassfish eclipselink

Using oracle xe with glassfish and eclipselink. Problems with persisting objects arise. After fresh restart of glassfish app works ok. If app is recompiled and redeployed via admin interface. Persi...

Chain of POST requests in Objective C (iPhone)

iphone ios objective-c http post

I need to send a server a set of requests with some data. The data in the subsequent requests will be determined based on the server response in the earlier requests. I do not want to use synchronous

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.