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

Obtaining a Bool out of [Maybe Bool] which is guaranteed to contain at least one 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 return 是左操作数,如果它是 Just something 而不管 something 是什么,所以 allTrueOrNothing将 return True 即使它的输入是 [Just True, Just False].

我完成任务的cleanest/most惯用方法是什么?

我看到我可以简单地 filter 输出 Nothing,然后 and 一起输出 Just,像这样:

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

但我更想知道是否有办法让那些 Maybe Bool 表现得像 Monoid 知道其 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 进行总结。

(请注意,这将 return True 列入全部 Nothing 列表,根据您的假设,这是“不可能”的情况。)

如果你绝对想使用幺半群,我想你可以这样做,但它有点麻烦。它将涉及将列表的每个元素包装在一些 newtype And = And (Maybe Bool) 中,然后定义相关的幺半群实例,然后 mconcating everying,最后解包。

未经测试的代码:

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

我会直接使用 all:

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

如果由于某种原因你必须有你描述的相位区别,那么你可以使用专业化 and = all id:

all and :: [Maybe Bool] -> Bool

最干净的方式是and . catMaybes

但是您想以 && 的方式使用知道其 Bool 内容的 Monoid。那是 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})