Extracting the first Just value from [Maybe a]
Asked Answered
D

3

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?

Dinnie answered 4/4, 2016 at 19:13 Comment(0)
F
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.

Foundry answered 4/4, 2016 at 19:17 Comment(4)
By switching from Maybe to First, you can just use fold.Cinder
Seems to need firstJust xs = asum xs <|> Nothing to handle empty lists.Allheal
@StevenShaw asum ([] :: [Maybe Int]) is Nothing so no problem with empty lists.Declarant
@Cinder This has the advantage that you could use Last with the same approach.Declarant
O
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
Outsider answered 5/4, 2016 at 9:27 Comment(0)
G
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.

Grendel answered 29/7 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.