13

Say I have a list like:

[Nothing, Just 1, Nothing, Just 2]

I want to get the first Just (non-error) value; in this case, it's Just 1. The only thing I can think of is:

firstJust xs = case filter isJust xs of
                 []     -> Nothing
                 Just x -> Just x

Is there a better/monad-generic way to do this?

0

3 Answers 3

28

msum from Control.Monad:

\> msum [Nothing, Just 1, Nothing, Just 2]
Just 1

or asum from Data.Foldable:

\> asum [Nothing, Just 1, Nothing, Just 2]
Just 1

Both are documented as:

The sum of a collection of actions, generalizing concat.

with signature:

msum :: (Foldable t, MonadPlus m) => t (m a) -> m a
asum :: (Foldable t, Alternative f) => t (f a) -> f a

and behave as above due to Maybe instance of Alternative.

Sign up to request clarification or add additional context in comments.

4 Comments

By switching from Maybe to First, you can just use fold.
Seems to need firstJust xs = asum xs <|> Nothing to handle empty lists.
@StevenShaw asum ([] :: [Maybe Int]) is Nothing so no problem with empty lists.
@dfeuer This has the advantage that you could use Last with the same approach.
4
import Data.Foldable (find)
import Data.Maybe (isJust)
import Control.Monad (join)

firstJust = join . find isJust

Usage:

> firstJust [Nothing, Just 1, Nothing, Just 2]
Just 1

> firstJust [Nothing, Nothing]
Nothing

Comments

1

Here are two other ways to accomplish it:

import Data.Foldable
import Data.Monoid

firstJust :: [Maybe a] -> Maybe a
firstJust = getFirst . foldMap First

That returns the leftmost Just value, or Nothing if it doesn't exist. You could get the rightmost value by replacing getFirst and First with getLast and Last.

import Data.Maybe

firstJust :: [Maybe a] -> Maybe a
firstJust = listToMaybe . catMaybes

That does the same thing, but it's not generalizable to types other than [Maybe a].

In general, if you ever find yourself writing something like filter isJust, you probably want either catMaybes or mapMaybe.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.