Search code examples
haskellalgebraic-data-typesmonoids

How/can this type be made into a Monoid instance


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.


Solution

  • 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 Applicatives, not just Stuff. The paper I linked discusses this idiom in more detail.