Suppose I have a type like
data Options = Options
{ _optionOne :: Maybe Integer
, _optionTwo :: Maybe Integer
, _optionThree :: Maybe String
} deriving Show
with many more fields. I would like to define a Monoid
instance for this type, for which the mempty
value is an Options
with all fields Nothing
. Is there a more concise way to write this than
instance Monoid Options where
mempty = Options Nothing Nothing Nothing
mappend = undefined
which would avoid the need to write a bunch of Nothing
s when my Options
has a ton more fields?
I would recommend just writing the Nothing
s, or even spelling out all the record fields explicitly, so you can be sure you don’t miss a case when adding new fields with a different mempty
value, or reordering fields:
mempty = Options
{ _optionOne = Nothing
, _optionTwo = Nothing
, _optionThree = Nothing
}
I haven’t tried it before, but it seems you can use the generic-deriving package for this purpose, as long as all the fields of your record are Monoid
s. You would add the following language pragma and imports:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)
import Generics.Deriving.Monoid
Add deriving (Generic)
to your data type and wrap all your non-Monoid
fields in a type from Data.Monoid
with the combining behaviour you want, such as First
, Last
, Sum
, or Product
:
data Options = Options
{ _optionOne :: Last Integer
, _optionTwo :: Last Integer
, _optionThree :: Maybe String
} deriving (Generic, Show)
Examples:
Last (Just 2) <> Last (Just 3)
= Last {getLast = Just 3}
First (Just 2) <> First (Just 3)
= First {getFirst = Just 2}
Sum 2 <> Sum 3
= Sum {getSum = 5}
Product 2 <> Product 3
= Product {getProduct = 6}
Then use the following function(s) from Generics.Deriving.Monoid
to make your default instance:
memptydefault :: (Generic a, Monoid' (Rep a)) => a
mappenddefault :: (Generic a, Monoid' (Rep a)) => a -> a -> a
In context:
instance Monoid Options where
mempty = memptydefault
mappend = ...