Given the following data type
{-# LANGUAGE GADTs #-}
data Response a where
ResponseMap :: HashMap Text (Sum Int) -> Response (HashMap Text (Sum Int))
ResponseSum :: Sum Int -> Response (Sum Int)
how would I derive a monoid instance for it? For the definition of mappend
I can pattern match on the constructors
(ResponseSum v1) `mappend` (ResponseSum v2) = undefined
(ResponseMap v1) `mappend` (ResponseMap v2) = undefined
and combine the values easily but I don't see how I would implement mempty
, or if it's indeed possible, or makes sense?
As you've noticed, you can't provide an instance Monoid (Response a)
, because you can't define mempty :: Response a
. Why not? Well, mempty
has to have type Response a
for all a
, including, say, Bool
. But you can't construct a value of type Response Bool
, only Response (HashMap Text (Sum Int))
and Response (Sum Int)
. So you won't be able to create a mempty
. This isn't a problem for mappend
, because you're given a Response a
, so you can check which a
you were given. But mempty
has nothing to analyze.
So what can you do? Well, first of all, you can provide an instance Semigroup (Response a)
. A semigroup is exactly a monoid without mempty
, so this is exactly what you want. As of GHC 8, you can find this type class in the base
package, in the module Data.Semigroup
; prior to that, you need to use the semigroups
package, with the same module name. Instead of mappend
, it uses the binary operator (<>)
. So you'd have
import Data.Semigroup
import Data.Monoid hiding ((<>))
-- ...
instance Semigroup (Response a) where
ResponseMap v1 <> ResponseMap v2 = ResponseMap $ v1 <> v2
ResponseSum v1 <> ResponseSum v2 = ResponseSum $ v1 <> v2
You can also provide specific Monoid
instances for the type indices that you can construct. With FlexibleInstances
, that looks like
{-# LANGUAGE FlexibleInstances #-}
instance Monoid (Response (HashMap Text (Sum Int))) where
mempty = ResponseMap mempty
mappend = (<>)
instance Monoid (Response (Sum Int)) where
mempty = ResponseSum mempty
mappend = (<>)
Now, for the cases where you do know what the unit is, you have a Monoid
instance.