6

I am trying to define an instance:

newtype Join a = Join { getJoin :: a -> Bool }
   deriving Generic

instance Monoid (Join a) where
   f <> g = ???
   mempty = ???

The goal is that the function foldMap Join should return True if all functions in the list are true, and false if all are not true.

I understand foldMap, and the instances of Sum and Product for Monoid but am otherwise quite new to writting newtype instances of Monoid. Any help in the right direction would be appreciated. Thank you.

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46

2 Answers2

9

You can make an new function that returns True if both the first and second function return True, by using (&&). Then the mempty is a Join function that is True for all input:

instance Monoid (Join a) where
    Join f <> Join g = Join (\x -> f x && g x)
    mempty = Join (const True)

Since the introduction of the Semigroup, the (<>) function is an instance of the Semigroup however:

import Control.Applicative(liftA2)

instance Semigroup (Join a) where
    Join f <> Join g = Join (liftA2 (&&) f g)

instance Monoid (Join a) where
    mappend = (<>)
    mconcat js = Join (\x -> all (($ x) . getJoin) js)
    mempty = Join (const True)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • That makes sense. And I assume using && determines the output of the Bool. I was trying to pattern match for the cases of Join f <> Join g, but that is the wrong approach. I'll have to study what Semigroup is as I haven't encountered it much. Thanks for the help. – Thunderbird Jul 19 '20 at 16:37
  • 1
    @Thunderbird `Semigroup` only defines an associative operation `<>`, and `Monoid` adds a unit `mempty` to that. So a semigroup is a "monoid without mandating a unity". – chi Jul 19 '20 at 17:13
1

Your type Join a is isomorphic to a -> All, which already has the Monoid instance that you want. As such, you can get the instance on your type without having to implement anything yourself, like this:

{-# LANGUAGE DerivingVia #-}

import Data.Monoid

newtype Join a = Join { getJoin :: a -> Bool }
   deriving Generic -- not needed for the Monoid instance; I only left it here to show you how to do two different kinds of deriving on the same type, since you derived this already in your question
   deriving (Semigroup, Monoid) via a -> All
Iceland_jack
  • 6,848
  • 7
  • 37
  • 46