Reputation: 929
Let's say I have a monoid defined as this:
data TotalLine = TotalLine { totalQuantity :: Int, orderTotal :: Float }
instance Monoid TotalLine where
mempty = zero
mappend = add
As totalQuantity and orderTotal are both numbers they also form a monoid under (+) Is there any way to define
add :: TotalLine -> TotalLine -> TotalLine
so, Can I just propagate the mappend call on each field instead of having to manually define something like
add line1 line2 =
TotalLine {
totalQuantity = totalQuantity line1 + totalQuantity line2,
orderTotal = orderTotal line1 + orderTotal line2
}
Upvotes: 3
Views: 101
Reputation: 3080
There's generic-deriving
package matches exactly your requirement:
{-# LANGUAGE DeriveGeneric #-}
import Generics.Deriving.Monoid
import GHC.Generics
data T = T {str :: String, str' :: String}
deriving (Generic, Show)
main = undefined
instance Monoid T where
mempty = memptydefault
mappend = mappenddefault
And some result from ghci:
> T "a" "b" `mappend` T "c" "d"
< T {str = "ac", str' = "bd"}
But it doesn't work out-of-box for your TotalLine
data type, since both Int
and Float
do not have an instance of Monoid
.
Also add a dependency just for that purpose is not so economic. You'd better implement the Monoid
instance manually.
There has been some discussion on why GHC cannot derive Monoid instances, it turned out in general such derived instance may not be unique. But in the special case when the data type have only one constructor with concrete and monoidal fields the derived instance is unique.
Upvotes: 4
Reputation: 10941
If you're fine changing the type a little (but that is still isomorphic to yours), you could try this:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Data.Monoid
newtype TotalLine = TotalLine (Sum Int, Sum Float) deriving Monoid
Upvotes: 3
Reputation: 2653
Note there are several monoids with domain Int (Nat, Float, ..)
so I would find it highly confusing to read code that has instance Monoid Int
. Then mappend
could be any of plus, times, max, min, and a lot more.
Upvotes: 7