Skip to content

Instantly share code, notes, and snippets.

@parsonsmatt
Last active May 7, 2017 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parsonsmatt/7711310eb12b7d72b080706f4f1af32f to your computer and use it in GitHub Desktop.
Save parsonsmatt/7711310eb12b7d72b080706f4f1af32f to your computer and use it in GitHub Desktop.
Which default monoid instance would you want?

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.

Option 1

Delegate to the Monoid instance for the values.

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)

Option 2:

Use the 'Alternative' instance for the functor.

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 (<|>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment