Semigroup instance for Data.Map


I was doing a bit of work with a Map from purescript-ordered-collections and I needed to combine maps in a way that also combined the values of the map with unionWith (<>)

I ended up writing a newtype to get this behaviour, but it seems like something like this probably exists and I just don’t know how to find it? Maybe I shouldn’t be using a map here at all… (NB: I know Collection is an awful name)

newtype Collection k v = Collection (Map k v)

instance semigroupCollection :: (Semigroup v, Ord k) => Semigroup (Collection k v) where
  append (Collection a) (Collection b) = M.unionWith (<>) a b # Collection

instance monoidCollection :: (Ord k, Semigroup (Collection k v)) => Monoid (Collection k v) where
  mempty = Collection M.empty

As you probably already know, Map has a Semigroup instance where append = union which isn’t what you want.

To get something similar To unionWith (<>) without the Newtype, you could do something like this:

import Prelude hiding ((<>))
import Data.Semigroup as Semigroup

myAppend = unionWith Semigroup.append

infixr 5 myAppend as <>

Side note - there is an ongoing discussion to make Semigroup / Monoid instances more flexible by depending on the internal type instance (I’m linking to the specific @goodacre.liam comment there):

This would be more consistent with for example instances of Foreign.Object or Maybe.

We can have an Alt instance (which is missing) in the case of both Data.Map and Foreign.Object which is right-biased (or left-biased) then I think.

1 Like