Ответ 1
Функция listToMaybe
в Data.Maybe
выглядит довольно хорошо:
tryPick f = listToMaybe . mapMaybe f
Существует стандартная функция tryPick, если F #, которая возвращает первое (слева направо, если вообще) успешное применение функции на элементе список. Я прыгаю, есть такая стандартная функция, как в Haskell. я попробовал Hoogle и ничего не нашел.
Я новичок в Haskell, и я не уверен, какой правильный способ это сделать. Вы сделали бы это так:
tryPick:: (a -> Maybe b) -> [a] -> Maybe b
tryPick try xs = case Maybe.mapMaybe try xs of
[] -> Nothing
(x:_) -> Just x
?
Функция listToMaybe
в Data.Maybe
выглядит довольно хорошо:
tryPick f = listToMaybe . mapMaybe f
Вы хотите:
tryPick :: (a -> Maybe b) -> [a] -> Maybe b
tryPick f as = msum (map f as)
Я объясню, как это работает.
map f as
создает список возможных действий Maybe
, чтобы попробовать:
map f as :: [Maybe b]
msum
последовательно пытается выполнить их до тех пор, пока не будет выполнено одно успешное выполнение (возврат значения как Just
), или все они откажутся (вернув Nothing
). Например:
> msum [Nothing, Just 2, Just 3, Nothing]
Just 2
> msum [Nothing, Nothing]
Nothing
Обратите внимание, что тип msum
более общий, поэтому мы можем обобщить подпись на:
tryPick :: (MonadPlus m) => (a -> m b) -> [a] -> m b
Теперь это будет работать для любого MonadPlus
. Получайте удовольствие, узнав, что он делает для других типов MonadPlus
.
Это не обязательно самое простое решение, но я считаю важным подчеркнуть решение First
Monoid
. Я думаю, что это самый красивый.
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
Это также немедленно обобщается на любой Foldable
с точно таким же кодом
tryPick :: Foldable t => (a -> Maybe b) -> t a -> Maybe b
Foldable
экземпляры предоставляют способы "разбить" все элементы вместе, используя Monoid
s. First
Monoid
, определяемый как
newtype First a = First { getFirst :: Maybe a }
является специализацией Maybe
с операцией mappend
, которая выбирает "первый" или "самый левый" Just
.
Итак, объединяя их, getFirst . foldMap (First . f)
вычисляет вашу функцию (a -> Maybe b)
по всем a
в [a]
, а затем разбивает результаты вместе с правилом, победителем которого является "первый" Just
.
Я немного опаздываю на вечеринку, но здесь вариант ответа Дж. Абрахамсона, который использует прекрасную функцию ala'
Конор Макбрайд из newtype
пакет:
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
Это может показаться немного загадочным, но я нахожу, как он отделяет "сборный сосуд" (First
) от "схемы сбора" (foldMap
) и как от "функции предварительной обработки" (a -> Maybe b
) - все, скрывая newtype
упаковку и разворачивание - довольно красиво. ala
был в моем опыте прекрасным инструментом для создания красивого кода, и я бы хотел его подключить. Спасибо, Конор!