and and or
If we have a container of Bools, we can ask whether all its elements are
True or at least one element is True. That's and and or:
and, or :: Foldable t => t Bool -> Bool
and = getAll . foldMap All
or = getAny . foldMap Any
This uses the fact that Booleans with logical and or logical or as the
multiplication operation form a monoid. The unit for the and-monoid is True.
The unit for the or-monoid is False. The only problem is that when declaring a
type to be a monoid in Haskell, we need to commit to one operation, here and
or or. This is one limitation of type classes in Haskell that has been the
subject of extensive debate and the focus of some effort to find more flexible
solutions. The way to work around this limitation is to wrap the Bool type
into newtypes. The first newtype, Any, is essentially Bool with or as the
monoid multiplication. All is Bool with and as the monoid multiplication:
newtype Any = Any { getAny :: Bool }
newtype All = All { getAll :: Bool }
instance Semigroup Any where
Any x <> Any y = Any (x || y)
instance Semigroup All where
All x <> All y = All (x && y)
instance Monoid Any where
mempty = Any False
instance Monoid All where
mempty = All True
This is the same trick we used to define monoids over number types that used
addition or multiplication as the monoid operations. Those were our Sum and
Product monoids.
Here are and and or in action:
>>> and [True,False,True,True]
False
>>> and [True,True,True,True]
True
>>> or [True,False,False,False]
True
>>> or [False,False,False,False]
False