Re: [Haskell-cafe] commutative monoid?

On 6/25/11 1:34 AM, Evan Laforge wrote:
So there's a range of possible Monoid instances for each type,
More for some types than for others. For Maybe there are three: * always take the first/left value; * always take the last/right value; * or, use a semigroup operation defined on the values. The first two options are provided by the First and Last newtypes, and the third option is provided by the instance for Maybe itself (except that it spuriously requires a Monoid instance instead of just a semigroup). For integers there are at least four obvious ones (min, max, addition, multiplication), and numerous less obvious ones. For real numbers we can also add the logarithmic and exponential versions. Etc.
maybe they were chosen by historical happenstance rather than some kind of "principle monoid" (is there such a thing?). Is there a name for the thing that's like a monoid, but the operator is commutative too?
"Commutative monoid" :)
That would narrow down the implementation choices, e.g. Data.Map would have to combine the values. So it seems like if your operation is commutative, you can put it in "c-monoid" and not rely so much on the happenstance of the instances, since they are more constrained.
They're more constrained, but still not enough to make things unique. All four of the obvious ones I mentioned for integers are commutative. You could keep constraining things further. But how constrained do you want to be? In some sense, there will always be room for multiple interpretations. Occasionally things magic their way into being unique, but usually not. Much of the last century of physics has been spent debating between different interpretations of some extremely constrained systems. So far as I'm aware, there's still no consensus about which interpretation is "right".
And of course you are then free to reorder things, which is a nice bit of flexibility.
Indeed, commutativity is a wonderful property to take advantage of when you can.
So is there a typeclass for that?
There might be one hidden in one of the attempts at redesigning the numeric hierarchy (e.g., Numeric Prelude), but there's not a canonical typeclass for them. Unfortunately it's not really a good match for the typeclass system since it doesn't introduce any new operations, it only introduces laws--- which aren't verified nor enforced. Though, if you happen to know the property does hold, then you're free to take advantage of it. (E.g., for the Maybe instance which uses a semigroup operation, if the semigroup op is commutative then so is the monoid op.) -- Live well, ~wren

So is there a typeclass for that?
There might be one hidden in one of the attempts at redesigning the numeric hierarchy (e.g., Numeric Prelude), but there's not a canonical typeclass for them. Unfortunately it's not really a good match for the typeclass system since it doesn't introduce any new operations, it only introduces laws--- which aren't verified nor enforced.
Though, if you happen to know the property does hold, then you're free to take advantage of it. (E.g., for the Maybe instance which uses a semigroup operation, if the semigroup op is commutative then so is the monoid op.)
That's a good point about laws vs. typeclasses. Though I think if I were doing something that relied on commutativity for e.g. Maybe I would still define my own typeclass. That way at least there is documentation in the name without having to put in a comment everywhere saying "btw I'm relying on this xyz", and if I want to give the same treatment to some other types then I don't have to deal with newtype wrappers. While I'm sure 'map Newtype biglist' can be optimized away, I'm not so sure about 'Map.map (second Newtype) bigmap'. But in any case, it's fine the stdlib doesn't have one, because it's easy enough to write your own. thanks!

On Fri, Jun 24, 2011 at 11:13:46PM -0700, wren ng thornton wrote:
On 6/25/11 1:34 AM, Evan Laforge wrote:
So there's a range of possible Monoid instances for each type,
More for some types than for others. For Maybe there are three:
* always take the first/left value; * always take the last/right value; * or, use a semigroup operation defined on the values.
Actually, there are (at least) four: there's also the one where mappend = liftA2 mappend, i.e. introduce potential failure into a monoid operation defined on the values. I wrote about it here: http://byorgey.wordpress.com/2011/04/18/monoids-for-maybe/ -Brent

On Sat, Jun 25, 2011 at 2:02 PM, Brent Yorgey
Actually, there are (at least) four: there's also the one where mappend = liftA2 mappend, i.e. introduce potential failure into a monoid operation defined on the values. I wrote about it here:
Just out of curiosity, what was the problem that wanted this kind of monoid for the solution? I always find concrete examples useful in addition to the abstract explorations and toy examples. It's Map not Maybe, but in my case, I have "damage", which is user-modified data that will have to be recomputed. Given a Map from IDs to damage ranges, mappending two bits of damage means the ranges (themselves monoids of course) also have to be mappended. The case for not lifting is that elsewhere, during said recomputation, I accumulate some intermediate results for display. It's actually a bug if two results share the same ID, but in any case it wouldn't make sense to merge them, and the value type isn't in monoid.

On Sat, Jun 25, 2011 at 02:34:47PM -0700, Evan Laforge wrote:
On Sat, Jun 25, 2011 at 2:02 PM, Brent Yorgey
wrote: Actually, there are (at least) four: there's also the one where mappend = liftA2 mappend, i.e. introduce potential failure into a monoid operation defined on the values. I wrote about it here:
Just out of curiosity, what was the problem that wanted this kind of monoid for the solution? I always find concrete examples useful in addition to the abstract explorations and toy examples.
To be honest, I don't remember! -Brent
participants (3)
-
Brent Yorgey
-
Evan Laforge
-
wren ng thornton