Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
4262af36
by L0neGamer at 2026-06-30T21:38:56-04:00
9 changed files:
- + changelog.d/generically-mconcat
- libraries/base/changelog.md
- libraries/ghc-internal/src/GHC/Internal/Generics.hs
- + testsuite/tests/generics/T27245.hs
- + testsuite/tests/generics/T27245.stdout
- testsuite/tests/generics/all.T
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
Changes:
| 1 | +section: base
|
|
| 2 | +synopsis: Add definition for Generically's `mconcat` in terms of the type variable's Semigroup instance instead of using the generically derived version.
|
|
| 3 | +issues: #27245
|
|
| 4 | +mrs: !16011 |
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | * Add `Data.List.NonEmpty.{zip{3..7},zipWith{3..7},unzip{3..7}}` ([CLC proposal #409)(https://github.com/haskell/core-libraries-committee/issues/409))
|
| 7 | 7 | * Ensure that `Data.List.elem` and `notElem` can be specialized even when no list fusion happens. ([CLC proposal #412)(https://github.com/haskell/core-libraries-committee/issues/412))
|
| 8 | 8 | * Introduce `Data.Double` and `Data.Float` modules. ([CLC proposal #378](https://github.com/haskell/core-libraries-committee/issues/378))
|
| 9 | + * Change `Generically a`'s `Monoid` definition to require a `Semigroup` constraint, and define its `mconcat` using `(<>)` from that constraint. ([CLC proposal #413](https://github.com/haskell/core-libraries-committee/issues/413))
|
|
| 9 | 10 | |
| 10 | 11 | ## 4.23.0.0 *TBA*
|
| 11 | 12 | * Add `System.IO.hGetNewlineMode`. ([CLC proposal #370](https://github.com/haskell/core-libraries-committee/issues/370))
|
| 1 | -{-# OPTIONS_GHC -Wno-noncanonical-monoid-instances #-}
|
|
| 2 | - |
|
| 3 | 1 | {-# LANGUAGE CPP #-}
|
| 4 | 2 | {-# LANGUAGE DataKinds #-}
|
| 5 | 3 | {-# LANGUAGE DeriveFunctor #-}
|
| ... | ... | @@ -744,7 +742,7 @@ import GHC.Internal.Types hiding (Any) -- clashes with the Semigroup |
| 744 | 742 | import GHC.Internal.Ix ( Ix )
|
| 745 | 743 | import GHC.Internal.Base ( Alternative(..), Applicative(..), Functor(..)
|
| 746 | 744 | , Monad(..), MonadPlus(..), NonEmpty(..), String
|
| 747 | - , Semigroup(..), Void )
|
|
| 745 | + , Semigroup(..), Void)
|
|
| 748 | 746 | import GHC.Internal.Err (errorWithoutStackTrace)
|
| 749 | 747 | import GHC.Internal.Classes ( Eq(..), Ord(..) )
|
| 750 | 748 | import GHC.Internal.Enum ( Bounded, Enum )
|
| ... | ... | @@ -1437,6 +1435,10 @@ class Generic1 (f :: k -> Type) where |
| 1437 | 1435 | -- type/ like 'Generically' decouples the instance from the type
|
| 1438 | 1436 | -- class.
|
| 1439 | 1437 | --
|
| 1438 | +-- Note that if you don't generate parent and child instances using the same
|
|
| 1439 | +-- method, the result may be incongruous; for example, in previous versions
|
|
| 1440 | +-- `mconcat` didn't use the correct `(<>)`, instead preferring a Generic version.
|
|
| 1441 | +--
|
|
| 1440 | 1442 | -- @since base-4.17.0.0
|
| 1441 | 1443 | newtype Generically a = Generically a
|
| 1442 | 1444 | |
| ... | ... | @@ -1446,12 +1448,14 @@ instance (Generic a, Semigroup (Rep a ())) => Semigroup (Generically a) where |
| 1446 | 1448 | Generically a <> Generically b = Generically (to (from a <> from b :: Rep a ()))
|
| 1447 | 1449 | |
| 1448 | 1450 | -- | @since base-4.17.0.0
|
| 1449 | -instance (Generic a, Monoid (Rep a ())) => Monoid (Generically a) where
|
|
| 1451 | +instance (Generic a, Semigroup a, Monoid (Rep a ())) => Monoid (Generically a) where
|
|
| 1450 | 1452 | mempty :: Generically a
|
| 1451 | 1453 | mempty = Generically (to (mempty :: Rep a ()))
|
| 1452 | 1454 | |
| 1453 | - mappend :: Generically a -> Generically a -> Generically a
|
|
| 1454 | - mappend = (<>)
|
|
| 1455 | + -- https://github.com/haskell/core-libraries-committee/issues/324
|
|
| 1456 | + mconcat :: [Generically a] -> Generically a
|
|
| 1457 | + mconcat = foldr (coerce @(a -> a -> a) (<>)) mempty
|
|
| 1458 | + {-# INLINE mconcat #-}
|
|
| 1455 | 1459 | |
| 1456 | 1460 | -- | A type whose instances are defined generically, using the
|
| 1457 | 1461 | -- 'Generic1' representation. 'Generically1' is a higher-kinded
|
| 1 | + |
|
| 2 | +{-# LANGUAGE DerivingVia #-}
|
|
| 3 | +{-# LANGUAGE UndecidableInstances #-}
|
|
| 4 | +{-# LANGUAGE AllowAmbiguousTypes #-}
|
|
| 5 | +{-# LANGUAGE ScopedTypeVariables #-}
|
|
| 6 | + |
|
| 7 | +import GHC.Generics
|
|
| 8 | +import Data.Coerce
|
|
| 9 | +import Data.Semigroup
|
|
| 10 | +import Data.List.NonEmpty qualified as NE
|
|
| 11 | +import Control.Exception
|
|
| 12 | + |
|
| 13 | +main :: IO ()
|
|
| 14 | +main = do
|
|
| 15 | + let l1 = L [1]
|
|
| 16 | + l2 = L [2]
|
|
| 17 | + -- check append functions; this is a regression test for later down the line
|
|
| 18 | + print ("sappend", l1 <> l2)
|
|
| 19 | + print ("mappend", l1 `mappend` l2)
|
|
| 20 | + print ("gappend", l1 `gappend` l2)
|
|
| 21 | + -- when `mappend` is removed from `Monoid`, `mappend`'s definition will be
|
|
| 22 | + -- `mappend = (<>)` at the top level, removing the above `mappend = gappend` issue
|
|
| 23 | + |
|
| 24 | + -- check concat functions
|
|
| 25 | + -- We have the defined method functions, the generic variants, and the
|
|
| 26 | + -- default implementations written out again
|
|
| 27 | + let ls = [l1, l2]
|
|
| 28 | + lsNE = l1 NE.:| [l2]
|
|
| 29 | + print ("sconcat", sconcat lsNE)
|
|
| 30 | + print ("mconcat", mconcat ls)
|
|
| 31 | + print ("gsconcat", gsconcat lsNE)
|
|
| 32 | + print ("gmconcat", gmconcat ls)
|
|
| 33 | + print ("dsconcat", dsconcat lsNE)
|
|
| 34 | + print ("dmconcatMappend", dmconcatMappend ls) -- uses `mappend` which is incorrect, see above
|
|
| 35 | + print ("dmconcatSappend", dmconcatSappend ls)
|
|
| 36 | + print ("dmconcatUsingSconcat", dmconcatUsingSconcat ls)
|
|
| 37 | + |
|
| 38 | + let undefinedList = l1 : undefined
|
|
| 39 | + fOn s f = print ("strictness " <> s, f undefinedList)
|
|
| 40 | + (fOn "mconcat" mconcat `finally` -- derived instance is fine, shows
|
|
| 41 | + fOn "gmconcat" gmconcat `finally` -- this is too strict - using mappend on lists needs all lists
|
|
| 42 | + fOn "dmconcatMappend" dmconcatMappend `finally` -- also too strict for the above
|
|
| 43 | + fOn "dmconcatSappend" dmconcatSappend `finally` -- correct strictness, shows
|
|
| 44 | + fOn "dmconcatUsingSconcat" dmconcatUsingSconcat -- sconcat is too strict as well
|
|
| 45 | + ) `catch` \(_ :: SomeException) -> pure ()
|
|
| 46 | + |
|
| 47 | +newtype L a = L [a]
|
|
| 48 | + deriving (Generic, Show, Eq)
|
|
| 49 | + deriving Monoid via (Generically (L a))
|
|
| 50 | + |
|
| 51 | +-- semigroup instance not derived with Generically, so it could be mis-aligned
|
|
| 52 | +-- with generic monoid definition
|
|
| 53 | +instance Semigroup (L a) where
|
|
| 54 | + L [] <> l = l
|
|
| 55 | + l <> _ = l
|
|
| 56 | + |
|
| 57 | +-- generic (<>)
|
|
| 58 | +gappend :: forall a . (Generic a, Semigroup (Rep a ())) => a -> a -> a
|
|
| 59 | +gappend a b = to (from a <> from b :: Rep a ())
|
|
| 60 | + |
|
| 61 | +-- generic sconcat
|
|
| 62 | +gsconcat :: forall a . (Generic a, Semigroup (Rep a ())) => NE.NonEmpty a -> a
|
|
| 63 | +gsconcat = to . sconcat @(Rep a ()) . fmap from
|
|
| 64 | + |
|
| 65 | +-- generic mconcat
|
|
| 66 | +gmconcat :: forall a . (Generic a, Monoid (Rep a ())) => [a] -> a
|
|
| 67 | +gmconcat = to . mconcat @(Rep a ()) . fmap from
|
|
| 68 | + |
|
| 69 | +-- default sconcat
|
|
| 70 | +dsconcat :: forall a . Semigroup a => NE.NonEmpty a -> a
|
|
| 71 | +dsconcat (a NE.:| as) = go a as where
|
|
| 72 | + go b (c:cs) = b <> go c cs
|
|
| 73 | + go b [] = b
|
|
| 74 | + |
|
| 75 | +-- default mconcat using mappend
|
|
| 76 | +dmconcatMappend :: Monoid a => [a] -> a
|
|
| 77 | +dmconcatMappend = foldr mappend mempty
|
|
| 78 | + |
|
| 79 | +-- default mconcat using (<>), also the new generically impl
|
|
| 80 | +dmconcatSappend :: Monoid a => [a] -> a
|
|
| 81 | +dmconcatSappend = foldr (<>) mempty
|
|
| 82 | + |
|
| 83 | +-- incorrect impl, too strict in the spine
|
|
| 84 | +dmconcatUsingSconcat :: Monoid a => [a] -> a
|
|
| 85 | +dmconcatUsingSconcat as = case as of
|
|
| 86 | + [] -> mempty
|
|
| 87 | + x : xs -> sconcat (x NE.:| xs) |
| 1 | +("sappend",L [1])
|
|
| 2 | +("mappend",L [1,2])
|
|
| 3 | +("gappend",L [1,2])
|
|
| 4 | +("sconcat",L [1])
|
|
| 5 | +("mconcat",L [1])
|
|
| 6 | +("gsconcat",L [1,2])
|
|
| 7 | +("gmconcat",L [1,2])
|
|
| 8 | +("dsconcat",L [1])
|
|
| 9 | +("dmconcatMappend",L [1,2])
|
|
| 10 | +("dmconcatSappend",L [1])
|
|
| 11 | +("dmconcatUsingSconcat",L [1])
|
|
| 12 | +("strictness mconcat",L [1])
|
|
| 13 | +("strictness dmconcatSappend",L [1]) |
| ... | ... | @@ -50,3 +50,4 @@ test('T19819', normal, compile_and_run, ['']) |
| 50 | 50 | test('T21185', normal, compile, [''])
|
| 51 | 51 | test('T25148a', normal, compile, [''])
|
| 52 | 52 | test('T25148b', normal, compile, [''])
|
| 53 | +test('T27245', normal, compile_and_run, ['']) |
| ... | ... | @@ -11056,7 +11056,7 @@ instance GHC.Internal.Base.Monoid ghc-internal-10.100.0:GHC.Internal.Event.Inter |
| 11056 | 11056 | instance GHC.Internal.Base.Monoid ghc-internal-10.100.0:GHC.Internal.Event.Internal.Types.Lifetime -- Defined in ‘ghc-internal-10.100.0:GHC.Internal.Event.Internal.Types’
|
| 11057 | 11057 | instance forall k (f :: k -> *) (p :: k) (g :: k -> *). (GHC.Internal.Base.Monoid (f p), GHC.Internal.Base.Monoid (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:*:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11058 | 11058 | instance forall k2 k1 (f :: k2 -> *) (g :: k1 -> k2) (p :: k1). GHC.Internal.Base.Monoid (f (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:.:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11059 | -instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11059 | +instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Semigroup a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11060 | 11060 | instance forall k c i (p :: k). GHC.Internal.Base.Monoid c => GHC.Internal.Base.Monoid (GHC.Internal.Generics.K1 i c p) -- Defined in ‘GHC.Internal.Generics’
|
| 11061 | 11061 | instance forall k (f :: k -> *) (p :: k) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Base.Monoid (f p) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.M1 i c f p) -- Defined in ‘GHC.Internal.Generics’
|
| 11062 | 11062 | instance forall p. GHC.Internal.Base.Monoid p => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Par1 p) -- Defined in ‘GHC.Internal.Generics’
|
| ... | ... | @@ -11091,7 +11091,7 @@ instance forall m. GHC.Internal.Base.Monoid m => GHC.Internal.Base.Monoid (Data. |
| 11091 | 11091 | instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (GHC.Internal.STM.STM a) -- Defined in ‘GHC.Internal.STM’
|
| 11092 | 11092 | instance forall k (f :: k -> *) (p :: k) (g :: k -> *). (GHC.Internal.Base.Monoid (f p), GHC.Internal.Base.Monoid (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:*:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11093 | 11093 | instance forall k2 k1 (f :: k2 -> *) (g :: k1 -> k2) (p :: k1). GHC.Internal.Base.Monoid (f (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:.:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11094 | -instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11094 | +instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Semigroup a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11095 | 11095 | instance forall k c i (p :: k). GHC.Internal.Base.Monoid c => GHC.Internal.Base.Monoid (GHC.Internal.Generics.K1 i c p) -- Defined in ‘GHC.Internal.Generics’
|
| 11096 | 11096 | instance forall k (f :: k -> *) (p :: k) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Base.Monoid (f p) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.M1 i c f p) -- Defined in ‘GHC.Internal.Generics’
|
| 11097 | 11097 | instance forall p. GHC.Internal.Base.Monoid p => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Par1 p) -- Defined in ‘GHC.Internal.Generics’
|
| ... | ... | @@ -11316,7 +11316,7 @@ instance forall a. GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (GHC.I |
| 11316 | 11316 | instance GHC.Internal.Base.Monoid GHC.Internal.Event.Windows.EventData -- Defined in ‘GHC.Internal.Event.Windows’
|
| 11317 | 11317 | instance forall k (f :: k -> *) (p :: k) (g :: k -> *). (GHC.Internal.Base.Monoid (f p), GHC.Internal.Base.Monoid (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:*:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11318 | 11318 | instance forall k2 k1 (f :: k2 -> *) (g :: k1 -> k2) (p :: k1). GHC.Internal.Base.Monoid (f (g p)) => GHC.Internal.Base.Monoid ((GHC.Internal.Generics.:.:) f g p) -- Defined in ‘GHC.Internal.Generics’
|
| 11319 | -instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11319 | +instance forall a. (GHC.Internal.Generics.Generic a, GHC.Internal.Base.Semigroup a, GHC.Internal.Base.Monoid (GHC.Internal.Generics.Rep a ())) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Generically a) -- Defined in ‘GHC.Internal.Generics’
|
|
| 11320 | 11320 | instance forall k c i (p :: k). GHC.Internal.Base.Monoid c => GHC.Internal.Base.Monoid (GHC.Internal.Generics.K1 i c p) -- Defined in ‘GHC.Internal.Generics’
|
| 11321 | 11321 | instance forall k (f :: k -> *) (p :: k) i (c :: GHC.Internal.Generics.Meta). GHC.Internal.Base.Monoid (f p) => GHC.Internal.Base.Monoid (GHC.Internal.Generics.M1 i c f p) -- Defined in ‘GHC.Internal.Generics’
|
| 11322 | 11322 | instance forall p. GHC.Internal.Base.Monoid p => GHC.Internal.Base.Monoid (GHC.Internal.Generics.Par1 p) -- Defined in ‘GHC.Internal.Generics’
|