and and or
If we have a container of Bool
s, 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