20

Is there a more idiomatic way to implement the following? I feel like I'm missing a way to get rid of the lambda, but couldn't figure out a way to convert it to point-free. Maybe there is another non-applicative way as well that is more straight forward?

import Data.Maybe
import Control.Applicative

foldl (\x y -> pure (+) <*> x <*> y) (Just 0) [Just 3, Just 4]
-- Just 7

foldl (\x y -> pure (+) <*> x <*> y) (Just 0) [Just 3, Just 4, Nothing]
-- Nothing
Xavier Shay
  • 4,067
  • 1
  • 30
  • 54

5 Answers5

34

I'd just use sequence from Control.Monad:

> fmap sum $ sequence [Just 3, Just 4]
Just 7
> fmap sum $ sequence [Just 3, Just 4, Nothing]
Nothing

For the point-free form:

sumMaybe :: Num a => [Maybe a] -> Maybe a
sumMaybe = fmap sum . sequence
bheklilr
  • 53,530
  • 6
  • 107
  • 163
17

The most direct way to eliminate the lambda is to use liftA2; it's exactly the code you wrote

liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x y = pure f <*> x <*> y

foldl (liftA2 (+)) (Just 0) [Just 1, Just 2]

then we have a few choices for how to propagate the errors. This code has it that any Nothing will lead to a total failure. We can do that in two steps like @bhekilr suggested using sequence.

sum <$> sequence [Just 1, Just 2]     sum <$> sequence [Just 1, Nothing]
Just (sum  [1,2])                     sum <$> Nothing
Just 3                                Nothing

We can also use the fact that (+) induces a Monoid on the values in order to just "ignore" Nothings. Most literally that would be

import Data.Monoid

getSum $ foldMap (maybe mempty Sum) [Just 1, Just 2, Nothing]
-- equivalent to, but faster than
getSum . mconcat . map (maybe mempty Sum) $ [Just 1, Just 2, Nothing]
getSum . mconcat $ [Sum 1, Sum 2, Sum 0]
3

But we can also use catMaybe from Data.Monoid to do it in two steps

sum . catMaybes $ [Just 1, Just 2, Nothing]
sum [1, 2]
3
J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
12

I think foldM works well here.

import Control.Monad
sumMay = foldM (fmap . (+)) 0

I think it's the clearest as it maps (Ba duh duh ching) to what you'd do in pure code.

daniel gratzer
  • 52,833
  • 11
  • 94
  • 134
3

You can lift the (+) in the Maybe Monad with:

input> fold (liftM2 (+)) (Just 0) [Just 1, Just 2]
Just 3
input> fold (liftM2 (+)) (Just 0) [Just 1, Just 2, Nothing]
Nothing
Ingo
  • 36,037
  • 5
  • 53
  • 100
0
import Data.Maybe
import Data.List
sumMaybes = sum . catMaybes
Jeremy List
  • 1,756
  • 9
  • 16