На абстрактном уровне, первое непустое значение - это Monoid
, называемое First
. Однако оказывается, что если вы просто наивно поднимите значения IO
в First
, у вас возникнет проблема с action3
, , поскольку операция моноидального добавления по умолчанию строго соответствует IO
* 1010. *.
Вы можете получить ленивые моноидальные вычисления, используя тип FirstIO
из этого ответа . Это не будет лучше, чем ответ Федора Сойкина, но он подчеркивает (я надеюсь), как вы можете составить поведение из универсальных абстракций.
Помимо вышеупомянутой оболочки FirstIO
, вы можете найти эту функцию полезной :
guarded :: Alternative f => (a -> Bool) -> a -> f a
guarded p x = if p x then pure x else empty
Я просто скопировал его из Protolude , поскольку в base я не смог найти нужную функциональность. Вы можете использовать его, чтобы обернуть свои списки в Maybe
, чтобы они соответствовали FirstIO
:
> guarded (not . null) [] :: Maybe [Int]
Nothing
> guarded (not . null) [1, 2, 3] :: Maybe [Int]
Just [1,2,3]
Сделайте это для каждого действия в вашем списке действий и оберните их в FirstIO
.
> :t (firstIO . fmap (guarded (not . null))) <$> [action1, action2, action3]
(firstIO . fmap (guarded (not . null))) <$> [action1, action2, action3]
:: Num a => [FirstIO [a]]
В приведенном выше фрагменте GHCi я показываю только тип с :t
. Я не могу показать значение, так как FirstIO
не имеет экземпляра Show
. Суть, однако, в том, что теперь у вас есть список FirstIO
значений, из которых mconcat
выберет первое непустое значение:
> getFirstIO $ mconcat $ (firstIO . fmap (guarded (not . null))) <$> [action1, action2, action3]
Just [1,2,3]
Если вы хотите распаковать Maybe
, Вы можете использовать fromMaybe
из Data.Maybe
:
answer :: IO [Integer]
answer =
fromMaybe [] <$>
(getFirstIO $ mconcat $ (firstIO . fmap (guarded (not . null))) <$> [action1, action2, action3])
Это явно сложнее, чем ответ Федора Сойкина, но я очарован тем, как Haskell позволяет вам собирать нужные функции с помощью ' щелкая вместе существующие вещи, почти как кирпичи Le go.
Итак, на вопрос существует ли этот комбинатор? ответ в том, что он вроде как, но какая-то сборка требуется.