I have data type:
data Stuff s = Stuff { name :: s, idx :: Int }
And want to make this into a monoid w/ the following implementations:
tmappend :: Stuff s -> Stuff t -> Stuff (s,t)
tmappend s1 s2 = Stuff (name s1, name s2) (idx s1 + idx s2)
tzero :: Stuff ()
tzero = Stuff () 0
Note it's possible to get arbitrarily nested tuples through mconcat
.
But tmappend is currently violating the type signature of mappend
. Is this actually a monoid? Can it be made into one with a better type representation.
This is known as a lax monoidal functor. I highly recommend you read this paper which shows how Applicatives are one type of lax monoid and you can reformulate your type as an Applicative
and get an equivalent interface:
instance Applicative Stuff where
pure a = Stuff a 0
(Stuff f m) <*> (Stuff x n) = Stuff (f x) (m + n)
tmappend :: (Applicative f) => f a -> f b -> f (a, b)
tmappend fa fb = (,) <$> fa <*> fb
tzero :: (Applicative f) => f ()
tzero = pure ()
Notice that tmappend
and tzero
work for all Applicative
s, not just Stuff
. The paper I linked discusses this idiom in more detail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With