Proposal: Remove Semigroup and Monoid instances for Data.Map, Data.IntMap, Data.HashMap

Mario Blažević mblazevic at
Tue Feb 13 19:54:13 UTC 2018

+1. But whatever happened to your proposal from last May? I don't think 
there were any objections to it. Would the two proposals be combined, or 
have you decided to drop the previous one?

On 2017-05-25 12:55 PM, David Feuer wrote:
 > A lot of people have wrappers around Data.Map and Data.IntMap to give
 > them more useful (Semigroup and) Monoid instances. I'd like to add such
 > wrappers to containers. What we need to be able to do that are *names*
 > for the new modules. I can't think of any, so I'm reaching out to the
 > list. Please suggest names! Another question is whether we should take
 > the opportunity of new modules to modernize and streamline the API a
 > bit. I'd like, at least, to separate "safe" from "unsafe" functions,
 > putting the unsafe ones in .Unsafe modules.

On 2018-02-13 02:33 PM, David Feuer wrote:
> Many people have recognized for years that the Semigroup and Monoid
> instances for Data.Map, Data.IntMap, and Data.HashMap are not so
> great. In particular, when the same key is present in both maps, they
> simply use the value from the first argument, ignoring the other one.
> This somewhat counter-intuitive behavior can lead to bugs. See, for
> example, the discussion in Tim Humphries's blog post[*]. I would like
> do do the following:
> 1. Deprecate the Semigroup and Monoid instances for Data.Map.Map,
> Data.IntMap.IntMap, and Data.HashMap.HashMap in the next releases of
> containers and unordered-containers.
> 2. Remove the deprecated instances.
> 3. After another several years (four or five, perhaps?), make a major
> release of each package in which the instances are replaced with the
> following:
>    instance (Ord k, Semigroup v) => Semigroup (Map k v) where
>      (<>) = Data.Map.Strict.unionWith (<>)
>    instance (Ord k, Semigroup v) => Monoid (Map k v) where
>      mempty = Data.Map.Strict.empty
>    instance Semigroup v => Semigroup (IntMap v) where
>      (<>) = Data.IntMap.Strict.unionWith (<>)
>    instance Semigroup v => Monoid (IntMap v) where
>      mempty = Data.IntMap.Strict.empty
>    instance (Eq k, Hashable k, Semigroup v) => Semigroup (HashMap k v) where
>      (<>) = Data.HashMap.Strict.unionWith (<>)
>    instance (Eq k, Hashable k, Semigroup v) => Monoid(HashMap k v) where
>      mempty = Data.HashMap.Strict.empty
> Why do I want the strict versions? That choice may seem a bit
> surprising, since the data structures are lazy. But the lazy versions
> really like to leak memory, making them unsuitable for most practical
> purposes.
> The big risk:
> Someone using old code or old tutorial documentation could get subtly
> wrong behavior without noticing. That is why I have specified an
> extended period between removing the current instances and adding the
> desired ones.
> Alternatives:
> 1. Remove the instances but don't add the new ones. I fear this may
> lead others to write their own orphan instances, which may not even
> all do the same thing.
> 2. Write separate modules with newtype-wrapped versions of the data
> structures implementing the desired instances. Unfortunately, this
> would be annoying to maintain, and also annoying to use--packages
> using the unwrapped and wrapped versions will use different types.
> Manually wrapping and unwrapping to make the different types work with
> each other will introduce lots of potential for mistakes and
> confusion.
> Discussion period: three weeks.
> [*]
> _______________________________________________
> Libraries mailing list
> Libraries at

More information about the Libraries mailing list