我有一个类型为[Maybe SomeType]
的输入list
和类型为SomeType -> Bool
的谓词p
,我想回答这个问题"谓词p
是否适用于碰巧出现在输入中的所有SomeType
?">。
第一部分很简单:(map . fmap) p list
的类型是[Maybe Bool]
。
一个重要的信息是,我知道length list >= 1
和all isNothing list == False
都保持,所以(map . fmap) p list
中必须至少有一个Just True
。
但是我如何从列表中取出一个单独的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
放在一起,然后把and
和Just
放在一起,就像这样:
allTrueOrNothing' :: [Maybe Bool] -> Bool
allTrueOrNothing' = all fromJust . filter (fmap not isNothing)
但我更好奇的是,是否有一种方法可以让这些Maybe Bool
s的行为像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})