This proposal was, I think, originally suggested by Jules Bean. The idea is to add two functions to the Data.Monoid module, (+>) and (<+), corresponding to different uses of mappend. These should not be methods of the Monoid typeclass, but top-level functions.
I hope (but slightly doubt) that the visual nature of the two operators might help to counter the thought that monoids are just for gluing things together.
(+>) :: (Monoid a) => a -> a -> aa +> b = a `mappend` b(<+) :: (Monoid a) => a -> a -> aa <+ b = b `mappend` ainfixl 4 +>infixl 4 <+
Proposed deadline: two weeks.
If this looks reasonable, I'll attach darcs patches.
Trac metadata
Trac field
Value
Version
6.10.3
Type
Bug
TypeOfFailure
OtherFailure
Priority
normal
Resolution
Unresolved
Component
libraries/base
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
Edited
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Linked items
0
Link issues together to show that they're related or that one is blocking others.
Learn more.
I dislike the asymmetry. The ++ operator is not commutative but we don't artificially make it visually asymmetric. Why not take Ross's proposal and use <> as in Data.Sequence.
For the sake of the discussion, perhaps someone should also explain why we cannot just generalise the type of ++. There's a separate argument that we should not.
Also, what is the purpose of the flipped append? Why do we want to dedicate a symbol for flipped append? I use monoid operations a lot and I cannot think of a single use of flipped append.
What is wrong with generalizing the type of (++)? If it would not break too much code (on first glance, I'm not sure how it would), I think that that would be the best solution.
In my opinion as a newbie, there are far more confusing things for newbies than (++) being the mappend of monoids. And anyways, monoids is far from being the hardest thing to understand in haskell, once you read a few well written blog posts.
You can still learn to use ++ as the concatenation operator for strings, and discover later that there is more to it than it looks.
There is no universal convention that symmetric-looking operators should be commutative.
However, it is certainly a handy visual guide.
To address specific points:
(&&) looks commutative to me, and it *is* commutative, in the | free fragment of haskell. That's the fragment I most commonly reason about. You don't get many algebraic laws without that. Similarly (||).
(>>) looks uncommutative - it's not about repeated symbols, it's about the way the > symbols obviously 'point' in one direction. And, (>>) *is* noncommutative, of course.
Concrete advantages for (+>):
It has a symbol which visually indicates direction, and mappend is often a directional operation in some sense;
It can be 'naturally' flipped to yield <+; it's useful to have a short name for flip mappend.
The latter is the key point. The reason to use +> is so we can use <+.
I rejected using (++) not just because it looks too symmetric (that's not a big problem, it doesn't confuse people too much) but simply because the backwards-compatibility damage of changing the type of a very very widely used operator is very significant.
By contrast, (+>) is not used in any widely used libraries, certainly not in base, and the backwards-compatibility damage of adding a new symbol to Data.Monoid is relatively low. No worse than adding any other new function to an API.
I think it's useful for Endo a, where you sometimes want to postcompose and sometimes precompose functions.
I think it's useful for any monoid which represents something like the composition of a graphical image, where precomposition is putting something 'underneath' and postcomposition is putting something 'on top'.
I suspect you quite often get Monoids along those general lines in DSLs; I've certainly seen a view.
Of course the case for a special flipped operator can never be that strong; you can always just write an expression the other way around.
+1 vote for generalizing (++). I think flipped mappend is so rare that writing "flip (++)" is actually easier to read. And you can still define your own <+ and +> if you use them a lot.
I agree with generalizing (++). Are there any programs that will break because of that generalization? My argument for reusing (++) is that I'd like to keep the number of different operators I have to memorize (in particular when reading other people's code) to a minimum. I only think very fundamental type classes deserve operators (e.g. Monad, Functor, Monoid, Num).
I agree. Since (++) is mappend for a specific type, it makes much sense to just generalize (++) to work with arbitrary Monoid instances. There is also a poll about this topic which has a strong bias to making (++) the new mappend: http://doodle.com/4yrfd7qaw5man3rm.
Technical decisions are not best made by democracy. A poll is not interesting.
What is interesting is arguments of substance. (++) is a sensible choice but has backward-compatibility problems, and "confusing newbies with weird error messages" problems. It is only for those reasons that I didn't choose it.
If we're choosing something new, then +> has the advantage of being visually flippable, but <> has the advantage of being already used in (at least) two other monoids.
What are the exact backward-compatibility problems? I can only think of cases like
cat = (++)
which only matter because the monomorphism restriction is still in use.
The “confusing newbiews with weird error messages” problem is already present in several places. For example, the expression 1 + 'A' will make GHC complain that there is no instance for (Num Char). I think, it would be better to try to generate better error messages instead of increasing the number of operators.
Looks like an abandoned proposal. If that's not the case, please re-open and give a discussion summary and consensus decision, as described on the process page.
I don’t think that this proposal was abandoned. I suppose that just nobody made the next steps to make the proposed change become reality. Would anyone object if I’d declare “use <> with fixity infixr 6 <>” as the consensus and go on with producing a patch for the libraries, etc.?
Having a right associative operator for monoid makes it less error prone to use builder monoid like Data.Binary.Builder and Data.Text.Lazy.Builder as they should be used in a right associative way.
I think what needs to happen is that someone creates a patch and moves the ticket to the "please merge" stage, assuming that consensus was reached (I didn't check the discussion thread).
Iterations _10K__________20K__________30K________Left 10.1 (0.5) 24.4 (0.6) 40.0 (1.3)Right 8.9 (0.2) 22.7 (3.1) 31.2 (4.6)Format: runtime (stddev) all in milliseconds
So switching to right-associativity may actually improve performance slightly. Scaling doesn't seem to be quite linear, but that could be due to cache effects or suchlike; and it's also outside the scope of this proposal.
Getting rid of mappend would break a lot of packages, so that will take a while. Also, what should happen to mconcat? The naming of mappend makes somewhat sense, because [a] is the free Monoid, so mconcat is just the extension of that naming strategy. Should mconcat stay as is, or can anyone think of a better name?
Getting rid of mappend would break a lot of packages, so that will take a while.
Of course. But that’s why I think we should start to prepare for mappend removal now. ;-)
Also, what should happen to mconcat? The naming of mappend makes somewhat sense, because [a] is the free Monoid, so mconcat is just the extension of that naming strategy. Should mconcat stay as is, or can anyone think of a better name?
I don‘t like the m in mappend and mconcat because it’s not very descriptive. Sometimes it means “monoid” as in mappend and mconcat, and sometimes it means “monad” as in mzero, mplus, and msum. So it might be a good idea to change mconcat to concat, at least in the long run. The current concat is just mconcat of the [a] instance of Monoid, so it could just be removed then.
Proposal tickets are no longer needed as part of the library submissions process. Instead, a normal ticket should be created once consensus has been achieved. Please see the process description for details.
This operator is already exported from Data.Semigroup.
Library authors who have Monoid instances should also
provide Semigroup instances. That makes the <>
operator available.
Exporting <> from Data.Monoid would break any code
that uses the synonymous operator from Data.Semigroup.
And anyway, it's incorrect - this is really a semigroup
operator, Monoid only adds the identity element.
Really Monoid should be a subclass of Semigroup, but that
would be difficult to do at this stage. It is similar to
the situation with Functor and Monad, unfortunately.
We have had the discussion on the libraires list (the discussion took place before the new libs process was in place, but what happened does follow what the new library process calls for).
We've created a ticket on the trac (re-opened this ticket) with the patch and the link to the discussion
So now we just need the maintainer to apply it. That's GHC HQ, meaning Simon, Simon or Ian. If you're busy and want to delegate, I'm happy to take the blame myself! :-)
We've all been procrastinating on this for years. Can we please get this into base for the 7.4 release please? :-)
This is an old proposal. I was in favor of it back in the day,
but now I am opposed, due to the existence of the semigroups package:
For the record, I disagree. Having a semigroup class is fine, but as long as it is outside of base then we still need <> in base otherwise in practice all the other packages are just not going to provide semigroup instances. Later on, if we move semigroup into base, or move monoid out of base into a different core package then that'll all be nice, we can move <> into semigroup etc. But lets not hold back everything just because it's not perfect. Whatever solution we use to fix Applicative between Functor and Monad, we'll be able to use the same solution to fix Semigroup.
commit 7dfa17d4ed8fa7604cb681e375133db4773b8910Author: Johan Tibell <johan.tibell@gmail.com>Date: Wed Jan 4 09:59:22 2012 -0800 Be explicit about what we import from Data.Monoid compiler/ghci/RtClosureInspect.hs | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
base:
commit 556d174a555b993176e6766017b984e7a096a327Author: Johan Tibell <johan.tibell@gmail.com>Date: Tue Aug 16 11:40:34 2011 +0200 Add <> as an alias for mappend Data/Monoid.hs | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
pretty:
commit f615a6aeb4abcdbfbfab67b0a82cef9a9a65ec49Author: Johan Tibell <johan.tibell@gmail.com>Date: Wed Jan 4 10:06:50 2012 -0800 Add note explaining why we use a different <> Text/PrettyPrint/HughesPJ.hs | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
We will have to separately sort out the pretty package needs to change if it wants to use the Data.Monoid.(<>) operator.
If it won't generalise Text.Pretty.<>, then I wonder why we don't name it ++ instead (and remove the current ++ definition).
I guess the main argument against doing so would be error messages for beginners, but perhaps better error messages and/or helium are the answer to that.