[GHC] #1460: Problem with Monoid instance of Data.Map
GHC
ghc-devs at haskell.org
Fri Aug 25 00:44:48 UTC 2017
#1460: Problem with Monoid instance of Data.Map
-------------------------------------+-------------------------------------
Reporter: ahey@… | Owner: (none)
Type: proposal | Status: closed
Priority: normal | Milestone: Not GHC
Component: libraries/base | Version: 6.6.1
Resolution: fixed | Keywords: Data.Map
| Monoid
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by andrewthad):
I'd like to bring this up again. Especially since `Semigroup`, in the next
two years, is going to end up as a superclass of `Monoid`. The even better
instance would be:
{{{
instance (Ord k, Semigroup v) => Monoid (Map k v)
}}}
In the mailing list that Joachim linked to, this kind of change seemed to
attract pretty broad support. I think the only reasonable way to make this
change without introducing silent breakage would be to release a
`containers-0.6` in which `Map` has no `Monoid` instance. Then, in
`containers-0.7`, the new instance would be introduced. This is precisely
what Henning proposed in
https://mail.haskell.org/pipermail/libraries/2012-April/017749.html, and I
think it's the best option available for switching out this instance.
These two release would need to be at least a year apart. We would
probably like for their to be a stackage lts between them that had
`containers-0.6`. And we would probably want to wait for `Semigroup` to
actually become a superclass of `Monoid` in `base` before releasing
`semigroups-0.7`, just because it's really annoying when you end up with a
`(Semigroup v, Monoid v)` constraint sometimes.
I would like to draw attention to the two arguments against making this
change:
1. It would break a lot of other packages
2. The behavior value-combining `Monoid` instance is easily recovered by
using `unionWith`. This reasoning roughly motivates this post:
https://mail.haskell.org/pipermail/libraries/2012-April/017745.html
Concerning argument (1), I offer no rebuttal. I'll only point out that,
with stackage, we now have a somewhat convenient way to attempt to measure
the breakage. But, no one has ever done this, and it might be worth
looking into.
Argument (2) I would like to challenge. In
http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html,
Gabriel Gonzalez uses, in a practical way, the behavior of data types that
can lift monoidal value into another monoid. Things like:
{{{
instance Monoid a => Monoid (IO a)
instance (Monoid a, Monoid b) => Monoid (a,b)
instance Monoid b => Monoid (a -> b)
}}}
These are useful because they give us `Monoid` instances for types like:
{{{
Int -> Bool -> Char -> IO ([Text],Ordering)
}}}
The value-combining `Monoid` instance for `Map` is in this same spirit. It
would allow us to trivially combine nested map with types like this:
{{{
type M = Map Int (Map Char (Map PersonId (Map ItemId [Text])))
mappend :: M -> M -> M
}}}
This behavior can be recovered by `unionWith`, but it's a length explicit
lifting:
{{{
deepAppend :: M -> M -> M
deepAppend = unionWith (unionWith (unionWith (unionWith (<>))))
}}}
I plan on continuing this tomorrow. I have a little more to say.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/1460#comment:8>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list