I'm writing an extensible records library for fun and profit. I have two potential 'Monoid' instances I can give my 'HashRecord' type. Which would you prefer to see?
The record type is essentially HashRecord f xs, where xs is a type level list of (key :: Symbol) =: (value :: *) pairs, and f is a type constructor that each entry in the record is contained in.
mempty does pure mempty for each field in the record, and mappend does liftA2 mappend for the fields in the record.
>>> mempty :: HashRecord' Identity ["foo" =: String, "bar" =: Any]
fromList [("foo", "\"\""), ("bar", "Any { getAny = False }")]
instance Monoid (HashRecord' f '[]) where
mempty = HashRecord mempty
mappend a _ = a -- empty + empty = empty
instance
( Monoid val
, Applicative f
, MapEntry key val
, Monoid (HashRecord' f xs)
) => Monoid (HashRecord' f (key =: val ': xs)) where
mempty = -- insert key (pure mempty) (mempty :: HashRecord' f xs)
mappend = -- Map.unionWith (liftA2 mappend)mempty returns Alternative.empty for each entry in the field, and mappend does <|> over each entry.
>>> mempty :: HashRecord' Maybe ["foo" =: Int, "bar" =: Bool]
fromList [("foo", "Nothing"), ("bar", "Nothing")]
instance Monoid (HashRecord' f '[]) where
mempty = HashRecord mempty
mappend a _ = a -- empty + empty = empty
instance
( Alternative f
, MapEntry key val
, Monoid (HashRecord' f xs)
) => Monoid (HashRecord (key =: val ': xs)) where
mempty = -- insert key Alternative.empty (mempty :: HashRecord xs)
mappend = -- Map.unionWith (<|>)