#haskell #functional-programming #monads #maybe #monadplus
Вопрос:
У меня есть входные list
данные типа [Maybe SomeType]
и предикат p
типа SomeType -> Bool
, и я хочу ответить на вопрос «Сохраняется ли предикат p
для всех SomeType
s, которые оказались во входных данных?».
Первая часть проста: (map . fmap) p list
имеет тип [Maybe Bool]
.
Одна важная информация заключается в том, что я это знаю, length list >= 1
и all isNothing list == False
обе они верны, так что должен быть хотя бы Just True
вход (map . fmap) p list
.
Но как мне вытащить один Bool
из этого списка?
Я подумал, что мог бы воспользоваться преимуществами сворачивания (например, через foldl
) и Maybe
, MonadPlus
например, сделать что-то вроде следующего:
allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromJust . foldl mplus mzero
но это не совсем верно, потому mplus
что возвращает левый операнд, если он Just something
независимо от того, что something
есть, поэтому allTrueOrNothing
вернется True
, даже если его входные [Just True, Just False]
данные .
Каков самый чистый/наиболее идиоматичный способ, которым я могу выполнить эту задачу?
Я вижу, что я мог бы просто filter
вывести Nothing
s, а затем and
вместе Just
s, что-то вроде этого:
allTrueOrNothing' :: [Maybe Bool] -> Bool
allTrueOrNothing' = all fromJust . filter (fmap not isNothing)
Но мне было более любопытно узнать, есть ли способ заставить этих Maybe Bool
людей вести себя так, как будто Monoid
они знают о его Bool
содержании.
Комментарии:
1. Можете ли вы получить представление о
Nothing
значениях перед нанесением на картуp
?all p . catMaybes
. Посколькуall p [] == True
не имеет значения , есть ли хотя бы одно не —Nothing
значение в исходном вводе.
Ответ №1:
Я бы просто использовал all
напрямую:
all . all :: (a -> Bool) -> [Maybe a] -> Bool
Если по какой-то причине вам необходимо различать фазы, которые вы описываете, вы можете использовать специализацию and = all id
:
all and :: [Maybe Bool] -> Bool
Комментарии:
1. Мне потребовалась секунда, чтобы понять, что это работает, потому
[Maybe a]
что имеет формуf (g a)
, в которойf
и то, иg
другое складывается. Умно (возможно, немного чересчур; -), но мне это нравится).2.
Foldable
Сочиняете ли вы вообще?3. @chepner Да, через
foldMap = foldMap . foldMap
.4. @DanielWagner, конечно, проверяет тип, но я не был уверен, что существует какой-то закон, который может быть нарушен в зависимости от моноида.
5. @chepner
Foldable
Законы не очень интересны: если вы определяете толькоfoldMap
, все законы удовлетворяются реализациями других методов по умолчанию, потому что законы связываютFoldable
методы только друг с другом.
Ответ №2:
Кажется, это работает:
> and . catMaybes $ [Just False, Nothing, Just False]
False
> and . catMaybes $ [Just False, Nothing, Just True]
False
> and . catMaybes $ [Just True, Nothing, Just True]
True
Вы можете использовать catMaybes
для преобразования списка в [Bool]
и and
для завершения.
(Обратите внимание, что это вернется True
в список всех Nothing
, что, согласно вашим предположениям, является «невозможным» случаем.)
Если вы абсолютно хотите использовать моноид, я думаю, вы можете это сделать, но это немного громоздко. Это включало бы в себя обертывание каждого элемента списка в некоторые newtype And = And (Maybe Bool)
, затем определение соответствующего экземпляра моноида, затем mconcat
повторение и, наконец, разворачивание.
Непроверенный код:
newtype And = And (Maybe Bool)
instance Semigroup And where
And Nothing <> x = x
x <> And Nothing = x
And (Just a) <> And (Just b) = And (Just (a amp;amp; b))
instance Monoid And where
mempty = Nothing
allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromMaybe False . coerce . mconcat @And . coerce
Комментарии:
1.
newtype And = And (Maybe Bool)
?2. @AlexeyRomanov Я отредактировал с соответствующими экземплярами. Надеюсь, теперь это более ясно.
3.
newtype And = And (Maybe Bool) deriving (Semigroup, Monoid) via Maybe All
Ответ №3:
Самый чистый способ-это and . catMaybes
.
Но вы хотели использовать Моноид, который знает о его Bool
содержании, в amp;amp;
некотором роде. Это All
:
> foldMap (fmap All) [Just True,Nothing,Just False]
Just (All {getAll = False})
> foldMap (fmap All) [Just True,Nothing,Just True]
Just (All {getAll = True})
Комментарии:
1. Принимая это, поскольку в нем более подробно рассматривается вопрос, имхо, несмотря на то, что ответ
Foldable
, основанный на примереMaybe
, тоже велик.2. @Enlico вы имели в виду «помимо», возможно, вместо «несмотря»? но в любом случае это не так. он использует
instance Monoid a => Monoid (Maybe a)
иinstance Foldable []
:foo :: (a -> Maybe All) -> [a] -> Maybe All ; foo = foldMap
также работает. (ср.foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
).:)3. Нет, я имел в виду, несмотря ни на что. Проблема в том, что я по ошибке написал вместо другого прямо перед ответом . Я имел в виду ответ Даниэля Вагнера.