Reputation: 19690
There is a standard tryPick function if F# that returns the first (from left-to-right if any at all) successful application of a function on an element of a list. I am hopping there is a standard function like that in Haskell. I tried Hoogle and didn't find anything.
I am new to Haskell and I am not sure what the right way of doing it is. Would you do it like this:
tryPick:: (a -> Maybe b) -> [a] -> Maybe b
tryPick try xs = case Maybe.mapMaybe try xs of
[] -> Nothing
(x:_) -> Just x
?
Upvotes: 13
Views: 1975
Reputation: 456
I'm coming a bit late to the party, but here's a variation on J. Abrahamson's answer that uses Conor McBride's lovely ala'
function from the newtype
package:
import Control.Newtype (ala')
import Data.Foldable (Foldable, foldMap)
import Data.Monoid (First(..))
tryPick :: (Foldable t) => (a -> Maybe b) -> t a -> Maybe b
tryPick = ala' First foldMap
This may seem a bit cryptic, but I find the way it decouples the "collection vessel" (First
) from the "collection scheme" (foldMap
) and both from the "preprocessing function" (a -> Maybe b
) -- all while hiding newtype
wrapping and unwrapping -- rather beautiful. ala
has been in my experience a fine tool for creating beautiful code, and I'd like to plug it. Thanks, Conor!
Upvotes: 8
Reputation: 35089
You want:
tryPick :: (a -> Maybe b) -> [a] -> Maybe b
tryPick f as = msum (map f as)
I'll explain how this works.
map f as
produces a list of possible Maybe
actions to try:
map f as :: [Maybe b]
msum
tries them sequentially until one succeeds (returning the value as a Just
) or they all fail (returning a Nothing
). For example:
> msum [Nothing, Just 2, Just 3, Nothing]
Just 2
> msum [Nothing, Nothing]
Nothing
Note that msum
's type is more general, so we can generalize the signature to:
tryPick :: (MonadPlus m) => (a -> m b) -> [a] -> m b
This will now work for any MonadPlus
. Have fun discovering what it does for other MonadPlus
types.
Upvotes: 16
Reputation: 74334
It's not necessarily the simplest solution, but I feel it important to highlight the First
Monoid
based solution. I think it's the prettiest.
import Data.Monoid
import Data.Foldable (Foldable, foldMap)
tryPick :: (a -> Maybe b) -> [a] -> Maybe b
tryPick f = getFirst . foldMap (First . f) -- this is just `foldMap f`
-- with the "firsty" Maybe Monoid
This is also immediately generalizable to any Foldable
with precisely the same code
tryPick :: Foldable t => (a -> Maybe b) -> t a -> Maybe b
Foldable
instances provide ways to "smash" all of the elements together in order using Monoid
s. The First
Monoid
defined as
newtype First a = First { getFirst :: Maybe a }
is a specialization of Maybe
with a mappend
operation that picks the "first" or "leftmost" Just
.
So, putting them together, getFirst . foldMap (First . f)
computes your (a -> Maybe b)
function over all of the a
s in the [a]
, then smashes the results together with the rule that the "first" Just
wins.
Upvotes: 12
Reputation: 177530
The listToMaybe
function in Data.Maybe
looks pretty good:
tryPick f = listToMaybe . mapMaybe f
Upvotes: 14