从[Maybe Bool]中获取一个Bool,保证至少包含一个Just



我有一个类型为[Maybe SomeType]的输入list和类型为SomeType -> Bool的谓词p,我想回答这个问题"谓词p是否适用于碰巧出现在输入中的所有SomeType?">

第一部分很简单:(map . fmap) p list的类型是[Maybe Bool]

一个重要的信息是,我知道length list >= 1all isNothing list == False都保持,所以(map . fmap) p list中必须至少有一个Just True

但是我如何从列表中取出一个单独的Bool?

我认为我可以利用折叠(例如通过foldl)和MaybeMonadPlus实例,做下面的事情:

allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromJust . foldl mplus mzero

,但这并不完全正确,因为mplus返回左操作数,如果它是Just something,不管something是什么,所以allTrueOrNothing将返回True,即使它的输入是[Just True, Just False]

我能完成任务的最干净/最地道的方法是什么?我看到我可以简单地把filterNothing放在一起,然后把andJust放在一起,就像这样:

allTrueOrNothing' :: [Maybe Bool] -> Bool
allTrueOrNothing' = all fromJust . filter (fmap not isNothing)

但我更好奇的是,是否有一种方法可以让这些Maybe Bools的行为像Monoid一样意识到它的Bool内容。

我会直接使用all:

all . all :: (a -> Bool) -> [Maybe a] -> Bool

如果出于某种原因你必须有你所描述的阶段区别,那么你可以使用专门化and = all id:

all and :: [Maybe Bool] -> Bool

这似乎可以工作:

> 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进行总结。

(注意,这将返回所有Nothing的列表上的True,这是一个"不可能的";根据你的假设来判断。)

如果你绝对想使用单oid,我想你可以这样做,但它有点麻烦。这将涉及到在一些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 && b))
instance Monoid And where
mempty = Nothing
allTrueOrNothing :: [Maybe Bool] -> Bool
allTrueOrNothing = fromMaybe False . coerce . mconcat @And . coerce

最干净的方法是and . catMaybes

但是你想使用Monoid,它知道它的Bool内容,以&&的方式。这是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})

最新更新