
Logical XOR is associative, also is XNOR. And they both have an identity element. Hence: newtype Even = Even {getEven :: Bool} newtype Odd = Odd {getOdd :: Bool} instance Semigroup Even where (<>) = (==) -- (==) over Bool == Logical XNOR instance Semigroup Odd where (<>) = (/=) -- (/=) over Bool == Logical XOR instance Monoid Even where mempty = True instance Monoid Odd where mempty = False So foldMap would determine the parity of the number of Trues. Also, Monoid lifted by Applicative is also Monoid. This might be useful: newtype AMonoid f a = AMonoid {getAMonoid :: f a} instance (Applicative f, Semigroup a) => Semigroup (AMonoid f a) where (<>) = liftA2 (<>) instance (Applicative f, Monoid a) => Monoid (AMonoid f a) where mempty = pure mempty