Question

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.

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top