Semigroup
Operator Alias
Left-Associative Mailing list discussion in progress on on {libraries,ghc-devs}@haskell.org
related reddit discussion on /r/haskell
Problem
With the implementation of prime:Libraries/Proposals/SemigroupMonoid, Semigroup
will become a superclass of Monoid
, and consequently Semigroup((<>))
will be re-exported alongside Monoid
from the Prelude
module.
-- reduced/simplified definition
class Semigroup a where
(<>) :: a -> a -> a
infixr 6 <>
The infixr 6
-fixity for <>
was already introduced 4 years ago, when we added Data.Monoid.<>
as alias for mappend
(which differs from infixr 5 ++
). See also #3339 (closed) for some of the discussion that began in 2009 leading up to the final infixr 6 <>
decision.
<>
in pretty printing APIs
Conflicting fixities of However, there are a few popular pretty-printing modules which already define a <>
top-level binding for their respective semigroup/monoid binary operation. The problem now is that those <>
definitions use a different operator fixity/associativity:
-- pretty
module Text.PrettyPrint.Annotated.HughesPJ where
infixl 6 <>
infixl 6 <+>
infixl 5 $$, $+$
-- pretty
module Text.PrettyPrint.HughesPJ where
infixl 6 <>
infixl 6 <+>
infixl 5 $$, $+$
-- template-haskell
module Language.Haskell.TH.PprLib where
infixl 6 <>
infixl 6 <+>
infixl 5 $$, $+$
-- ghc
module Outputable
infixl 9 <>
infixl 9 <+>
infixl 9 $$, $+$
-- ghc
module Pretty where
infixl 6 <>
infixl 6 <+>
infixl 5 $$, $+$
On the other hand, the popular hackage:ansi-wl-pprint package does use right-associative operators:
module Text.PrettyPrint.ANSI.Leijen where
infixr 6 <>
infixr 6 <+>
Other pretty printers also using a infixr 6 <>, <+>
definition:
<>
's associativity in pretty-printing APIs
Changing Changing the fixity of pretty
's <>
would however results in a semantic change for code which relies on the relative fixity between <+>
and <>
as was pointed out by Duncan back in 2011 already:
So I was preparing to commit this change in base and validating ghc when I discovered a more subtle issue in the pretty package:
Consider
a <> empty <+> b
The fixity of
<>
and<+>
is critical:(a <> empty) <+> b = {- empty is unit of <> -} (a ) <+> b a <> (empty <+> b) = {- empty is unit of <+> -} a <> ( b)
Currently Text.Pretty declares
infixl 5 <>, <+>
. If we change them to beinfixr
then we get the latter meaning ofa <> empty <+> b
. Existing code relies on the former meaning and produces different output with the latter (e.g. ghc producing error messages like "instancefor" when it should have been "instance for").
Unsatisfying Situation Seeking a Long-term Solution
Consequently, it's confusing and bad practice to have a soon-to-be-in-Prelude <>
operator whose meaning depends on which import
s are currently in scope. Moreover, a module needs to combine pretty-printing monoids and other non-pretty-printing monoids, the conflicting <>
s operator needs to be disambiguated via module qualification or similiar techniques.
However, there also seems to be a legitimate use-case for a left-associative <>
operator.
Alternative Suggestions
David Terei suggests among other things to
Switch
<>
to infixr67 and<+>
to infixr56, some code can still break, but arguably code relying on unintuitive semantics (since somewhat odd<>
and<+>
have same precedence when both treat empty as identity).
resulting in
infixr 7 <>
infixr 6 <+>
infixr 5 $$, $+$
Proposed Solution
Leave Semigroup((<>))
as infixr 6
, and add a standardised left-associative alias for <>
to the Data.Semigroup
vocabulary, i.e.
module Data.Semigroup where
infixl 6 ><
-- | Left-associative alias for (right-associative) 'Semigroup' operation '(<>)'
(><) :: Semigroup a => a -> a -> a
(><) = (<>)
><
Bikesheds for .<>
<~>
<#>