diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,143 @@
-* 0.3.3.5 03 Dec 2014
+* 0.7.0.1: 29 January 2026
+
+  - Add some cautionary comments to `Regular` and `Semi`.  In
+    particular, `Regular` cannot be used to create a lawful semidirect
+    product of a monoid/semigroup with itself. ([#63](https://github.com/diagrams/monoid-extras/issues/63))
+
+* 0.7: 12 May 2025
+
+  - Updates to `Data.Monoid.Coproduct`:
+    - Fix `Eq` instance for monoid coproducts to take `mempty` into account
+    - `cop` implements coproduct universal map
+    - `untangleSemi`, like `untangle` but as a monoid homomorphism to semidirect product
+    - `toReducedAltList`, like `toAltList` but also gets rid of `mempty`
+  - New module `Data.Semigroup.Coproduct` with semigroup coproducts
+  - Remove `Data.Monoid.Coproduct.Strict`
+
+  Thanks to Sonat Süer (@sonatsuer) for the updates!
+
+* 0.6.5: 22 February 2025
+
+- New instance `Eq (m :+: n)` ([#59](https://github.com/diagrams/monoid-extras/issues/59))
+- New function `toAltList :: (m :+: n) -> [Either m n]`
+
+* 0.6.4: 10 February 2025
+
+- New instance `Action m a => Action [m] a` (thanks to Manuel Bärenz for the suggestion)
+
+* 0.6.3: 8 August 2024
+
+- New instances (thanks to Clinton Mead):
+  - `Action (First a) a`
+  - `Action Void a`
+  - `Action m a => Action (Identity m) a`
+
+- r1 (27 Jan 2025):
+  - Allow `base-4.21` and test on GHC 9.12.
+
+* 0.6.2: 20 Dec 2022
+
+- New class and newtypes in `Data.Monoid.Action` (thanks to Manuel Bärenz):
+    - `Torsor` class for transitive group actions
+    - `Regular` and `Conjugate` newtypes for groups acting on themselves
+
+- r1 (27 March 2023):
+  - allow `semigroupoids-6.0`
+  - allow `base-4.18` and test on GHC 9.6.
+
+- r2 (17 Oct 2023):
+  - allow `base-4.19` and test on GHC 9.8.
+
+- r3 (15 May 2024):
+  - allow `base-4.20` and test on GHC 9.10.
+
+* 0.6.1: 16 Nov 2021
+
+- Add more efficient `stimes` implementations for several `Semigroup`
+  instances.  Thanks to BlackCapCoder for the patch!
+- Allow `base-4.16` and test on GHC 9.2.1.
+
+- r1: allow `base-4.16` in benchmarks
+- r2 (15 August 2022): allow `base-4.17` and test with GHC 9.4.
+
+* 0.6: 8 May 2021
+
+- Updates for GHC 8.10 and 9.0.
+- Drop support for GHC 8.2 or older.
+- Replace deprecated `Option` type with `Maybe`.
+
+* 0.5.1: 19 Oct 2019
+
+- New module Data.Monoid.Coproduct.Strict for a more efficient coproduct in
+  some use cases.
+- Update for GHC 8.8.
+- Drop support for GHC 7.8.
+
+* 0.5: 14 May 2018
+
+- Modernize Data.Monoid.WithSemigroup
+
+  It used to export a type class Monoid' with no methods and a single
+  instance, for use as a "poor man's constraint synonym" for the
+  combination of Monoid and Semigroup.  Now Monoid':
+
+    - Is a real constraint synonym, using ConstraintKinds.
+    - Is simply a synonym for Monoid under base-4.11 and later, in
+      which case Semigroup is already a superclass of Monoid.
+
+  This technically necessitates a major version bump but should not
+  cause any issues for packages that depend on monoid-extras, other
+  than potentially requiring the addition of a ConstraintKinds pragma
+  under GHC 7.8.
+
+* 0.4.4: 8 April 2018
+
+- Fix build on older (< 7.10) GHCs (thanks to George Wilson for the fix)
+
+* 0.4.3: 3 April 2018
+
+- Allow base-4.11
+- Fix compilation on GHC 8.4
+- Add more instances for Inf
+
+* 0.4.2: 16 July 2016
+
+- Additions to Data.Monoid.SemiDirectProduct (unSemi, tag, untag)
+
+- Hackage revision 1: allow semigroupoids-5.2
+- Hackage revision 2: allow base-4.10
+
+* 0.4.1.2: 16 June 2016
+
+- allow semigroupoids-5.1
+
+* 0.4.1: 8 June 2016
+
+- new modules Data.Monoid.SemiDirectProduct[.Strict].
+
+* 0.4.0.4: 14 February 2016
+
+- allow base-4.9 for GHC-8
+
+* 0.4.0.3: 10 November 2015
+
+- allow semigroups-0.18
+
+* 0.4.0.2: 16 September 2015
+
+- allow semigroups-0.17
+
+* v0.4.0.1
+
+- allow semigroupoids-5.0
+
+* 0.4: 19 April 2015
+
+  - add derived instances where possible:
+    Typeable, Data, Read, Eq, Ord, Functor, Foldable, Traversable
+  - allow base-4.8
+
+* 0.3.3.5: 03 Dec 2014
 
   - allow semigroups-0.15
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,9 +1,12 @@
-Copyright (c) 2012-2013, monoid-extras team:
+Copyright (c) 2012-2015, monoid-extras team:
 
   Daniel Bergey <bergey@alum.mit.edu>
+  Christopher Chalmers <c.chalmers@me.com>
   Nathan van Doorn <nvd1234@gmail.com>
   Daniil Frumin <difrumin@gmail.com>
   Hans Höglund <hans@hanshoglund.se>
+  Moritz Kiefer <moritz.kiefer@purelyfunctional.org>
+  Piyush P Kurur <ppk@cse.iitk.ac.in>
   Daniel Wagner <daniel@wagner-home.com>
   Ryan Yates <fryguybob@gmail.com>
   Brent Yorgey <byorgey@gmail.com>
diff --git a/benchmarks/SemiDirectProduct.hs b/benchmarks/SemiDirectProduct.hs
new file mode 100644
--- /dev/null
+++ b/benchmarks/SemiDirectProduct.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE CPP                        #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module Main where
+
+import           Criterion.Main
+
+#if !MIN_VERSION_base(4,8,0)
+import           Data.Monoid
+import           Data.Word
+#else
+import           Data.Monoid (Sum(..))
+#endif
+#if !MIN_VERSION_base(4,11,0)
+import           Data.Semigroup (Semigroup)
+#endif
+
+import           Data.Monoid.Action
+import qualified Data.Monoid.SemiDirectProduct        as L
+import qualified Data.Monoid.SemiDirectProduct.Strict as S
+
+newtype MyMonoid = MyMonoid (Sum Word) deriving (Semigroup, Monoid)
+
+instance Action MyMonoid () where
+  act _ = id
+  {-# NOINLINE act #-}
+
+main :: IO ()
+main = defaultMain
+       [ bench "mconcat/strict"   $ whnf mconcat strict
+       , bench "mconcat/lazy"     $ whnf mconcat lazy
+       , bench "strict/quotient"  $ whnf (S.quotient . mconcat) strict
+       , bench "lazy/quotient"    $ whnf (L.quotient . mconcat) lazy
+       ]
+  where strict :: [S.Semi () MyMonoid]
+        strict =  map (S.embed . MyMonoid . Sum) $ take 1000 [1..]
+        lazy   :: [L.Semi () (MyMonoid)]
+        lazy   =  map (L.embed . MyMonoid . Sum) $ take 1000 [1..]
diff --git a/monoid-extras.cabal b/monoid-extras.cabal
--- a/monoid-extras.cabal
+++ b/monoid-extras.cabal
@@ -1,9 +1,10 @@
 name:                monoid-extras
-version:             0.3.3.5
+version:             0.7.0.2
 synopsis:            Various extra monoid-related definitions and utilities
 description:         Various extra monoid-related definitions and utilities,
-                     such as monoid actions, monoid coproducts, \"deletable\"
-                     monoids, \"split\" monoids, and \"cut\" monoids.
+                     such as monoid actions, monoid coproducts, semi-direct
+                     products, \"deletable\" monoids, \"split\" monoids,
+                     and \"cut\" monoids.
 license:             BSD3
 license-file:        LICENSE
 extra-source-files:  CHANGES
@@ -13,6 +14,7 @@
 category:            Data
 build-type:          Simple
 cabal-version:       >=1.10
+tested-with:         GHC ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 || ==9.14.1
 
 source-repository head
   type: git
@@ -21,6 +23,8 @@
 library
   default-language:  Haskell2010
   exposed-modules:   Data.Monoid.Action,
+                     Data.Monoid.SemiDirectProduct,
+                     Data.Monoid.SemiDirectProduct.Strict
                      Data.Monoid.Coproduct,
                      Data.Monoid.Cut,
                      Data.Monoid.Deletable,
@@ -29,16 +33,28 @@
                      Data.Monoid.MList,
                      Data.Monoid.Recommend,
                      Data.Monoid.Split,
-                     Data.Monoid.WithSemigroup
+                     Data.Monoid.WithSemigroup,
+                     Data.Semigroup.Coproduct
 
-  build-depends:     base >= 4.3 && < 4.8,
-                     groups < 0.5,
-                     semigroups >= 0.8 && < 0.17,
-                     semigroupoids >= 4.0 && < 5
+  build-depends:     base >= 4.11 && < 4.23,
+                     groups < 0.6,
+                     semigroupoids >= 4.0 && < 6.1
 
   hs-source-dirs:    src
 
-  other-extensions:  DeriveFunctor,
-                     FlexibleInstances,
-                     MultiParamTypeClasses,
+  ghc-options: -Wall
+
+  other-extensions:  DeriveFunctor
+                     FlexibleInstances
+                     MultiParamTypeClasses
                      TypeOperators
+                     ConstraintKinds
+
+benchmark semi-direct-product
+  default-language:  Haskell2010
+  hs-source-dirs: benchmarks
+  main-is: SemiDirectProduct.hs
+  type: exitcode-stdio-1.0
+  build-depends: base          >= 4.3 &&  < 4.23
+               , criterion
+               , monoid-extras
diff --git a/src/Data/Monoid/Action.hs b/src/Data/Monoid/Action.hs
--- a/src/Data/Monoid/Action.hs
+++ b/src/Data/Monoid/Action.hs
@@ -14,9 +14,17 @@
 
 module Data.Monoid.Action
        ( Action(..)
+       , Regular(..)
+       , Conjugate(..)
+       , Torsor(..)
        ) where
 
+import           Data.Functor.Identity (Identity(Identity))
 import           Data.Semigroup
+import qualified Data.Semigroup as Semigroup
+import           Data.Group
+import qualified Data.Monoid as Monoid
+import           Data.Void (Void, absurd)
 
 ------------------------------------------------------------
 --  Monoid and semigroup actions
@@ -28,15 +36,19 @@
 --
 --   * @act mempty = id@
 --
---   * @act (m1 ``mappend`` m2) = act m1 . act m2@
+--   * @act (m1 \`mappend\` m2) = act m1 . act m2@
 --
 --   Semigroup instances are required to satisfy the second law but with
---   ('<>') instead of 'mappend'.  Additionally, if the type @s@ has
---   any algebraic structure, @act m@ should be a homomorphism.  For
---   example, if @s@ is also a monoid we should have @act m mempty =
---   mempty@ and @act m (s1 ``mappend`` s2) = (act m s1) ``mappend``
---   (act m s2)@.
+--   ('<>') instead of 'mappend'.
 --
+--   Additionally, if the type @s@ has any algebraic structure, @act
+--   m@ should typically be a homomorphism.  For example, if @s@ is
+--   also a monoid we should have @act m mempty = mempty@ and @act m
+--   (s1 \`mappend\` s2) = (act m s1) \`mappend\` (act m s2)@.  In
+--   particular, these laws are necessary for the semidirect product
+--   @Semi s m@ to be a valid semigroup/monoid.  For a more
+--   fine-grained treatment of these ideas, see the @lr-acts@ package.
+--
 --   By default, @act = const id@, so for a type @M@ which should have
 --   no action on anything, it suffices to write
 --
@@ -64,10 +76,14 @@
   act () = id
 
 -- | @Nothing@ acts as the identity; @Just m@ acts as @m@.
-instance Action m s => Action (Option m) s where
-  act (Option Nothing)  s = s
-  act (Option (Just m)) s = act m s
+instance Action m s => Action (Maybe m) s where
+  act Nothing  s = s
+  act (Just m) s = act m s
 
+-- | @act [a,b,c,...] = act a . act b . act c . ...@
+instance Action m s => Action [m] s where
+  act = flip (foldr act)
+
 -- | @Endo@ acts by application.
 --
 --   Note that in order for this instance to satisfy the @Action@
@@ -81,3 +97,64 @@
 instance Action (Endo a) a where
   act = appEndo
 
+instance Num a => Action Integer (Sum a) where
+  n `act` a = fromInteger n <> a
+
+instance Num a => Action Integer (Product a) where
+  n `act` a = fromInteger n <> a
+
+instance Fractional a => Action Rational (Sum a) where
+  n `act` a = Sum (fromRational n) <> a
+
+instance Fractional a => Action Rational (Product a) where
+  n `act` a = Product (fromRational n) <> a
+
+-- | An action of a group is "free transitive", "regular", or a "torsor"
+--   iff it is invertible.
+--
+--   Given an original value `sOrig`, and a value `sActed` that is the result
+--   of acting on `sOrig` by some `m`,
+--   it is possible to recover this `m`.
+--   This is encoded in the laws:
+--
+--   * @(m `'act'` s) `'difference'` s = m@
+--   * @(sActed `'difference'` sOrig) `'act'` sOrig = sActed@
+class Group m => Torsor m s where
+
+  -- | @'difference' sActed sOrig@ is the element @m@ such that @sActed = m `'act'` sOrig@.
+  difference :: s -> s -> m
+
+-- | Any monoid acts on itself by left multiplication.
+--   This newtype witnesses this action:
+--   @'getRegular' $ 'Regular' m1 `'act'` 'Regular' m2 = m1 '<>' m2@
+--
+--   Note that this typically does NOT satisfy the distributivity law
+--   @m `act` (m1 <> m2) = (m `act` m1) <> (m `act` m2)@, and hence
+--   cannot be used to form a lawful semidirect product of a monoid with itself.
+newtype Regular m = Regular {getRegular :: m}
+
+instance Semigroup m => Action m (Regular m) where
+  m1 `act` Regular m2 = Regular $ m1 <> m2
+
+instance Group m => Torsor m (Regular m) where
+  Regular m1 `difference` Regular m2 = m1 ~~ m2
+
+-- | Any group acts on itself by conjugation.
+newtype Conjugate m = Conjugate { getConjugate :: m }
+
+instance Group m => Action m (Conjugate m) where
+  m1 `act` Conjugate m2 = Conjugate $ m1 <> m2 ~~ m1
+
+instance Action (Semigroup.First a) a where
+  act (Semigroup.First m) _ = m
+
+instance Action (Monoid.First a) a where 
+  act (Monoid.First m) s = case m of
+    Nothing -> s
+    Just m' -> m'
+
+instance Action Void a where
+  act = absurd
+
+instance Action m s => Action (Identity m) s where
+  act (Identity m) = act m
diff --git a/src/Data/Monoid/Coproduct.hs b/src/Data/Monoid/Coproduct.hs
--- a/src/Data/Monoid/Coproduct.hs
+++ b/src/Data/Monoid/Coproduct.hs
@@ -1,12 +1,13 @@
-{-# LANGUAGE TypeOperators
-           , FlexibleInstances
-           , MultiParamTypeClasses
-  #-}
+{-# LANGUAGE DeriveDataTypeable    #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE LambdaCase #-}
 
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Coproduct
--- Copyright   :  (c) 2011 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2011-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
@@ -18,29 +19,79 @@
        ( (:+:)
        , inL, inR
        , mappendL, mappendR
+       , cop
        , killL, killR
+       , toAltList
+       , toReducedAltList
        , untangle
-
+       , untangleSemi
        ) where
 
-import Data.Either (lefts, rights)
+import Data.Function      (on)
 import Data.Semigroup
+import Data.Typeable
 
 import Data.Monoid.Action
+import Data.Monoid.SemiDirectProduct ( embed, inject, Semi, unSemi )
+import Data.Tuple (swap)
 
 -- | @m :+: n@ is the coproduct of monoids @m@ and @n@.  Values of
 --   type @m :+: n@ consist of alternating lists of @m@ and @n@
---   values.  The empty list is the identity, and composition is list
+--   values. The empty list is the identity, and composition is list
 --   concatenation, with appropriate combining of adjacent elements
---   when possible.
+--   and removing identities when possible.
 newtype m :+: n = MCo { unMCo :: [Either m n] }
-                  deriving Show
+  deriving (Typeable, Show)
 
+instance (Eq m, Eq n, Monoid m, Monoid n) => Eq (m :+: n) where
+  (==) = (==) `on` (normalizeEq . unMCo)
+
+-- | Extract a monoid coproduct to a list of @Either@ values.  The
+--   resulting list is guaranteed to be normalized, in the sense that
+--   it will strictly alternate between @Left@ and @Right@.
+toAltList :: (Semigroup m, Semigroup n) => (m :+: n) -> [Either m n]
+toAltList (MCo ms) = normalize ms
+
+-- | Extract a monoid coproduct to a list of @Either@ values.  The
+--   resulting list is guaranteed to be normalized, in the sense that
+--   it will strictly alternate between @Left@ and @Right@ and no identity
+--   element from @m@ or @n@ will occur in the list.
+toReducedAltList :: (Eq m, Eq n, Monoid m, Monoid n) => (m :+: n) -> [Either m n]
+toReducedAltList (MCo ms) = normalizeEq ms
+
+-- Normalize a list of @Either@ values by combining any consecutive
+-- values of the same type.
+normalize :: (Semigroup m, Semigroup n) => [Either m n] -> [Either m n]
+normalize = \case
+  (Left e1:Left e2 : es) -> normalize (Left (e1 <> e2) : es)
+  (Right e1:Right e2:es) -> normalize (Right (e1 <> e2) : es)
+  []  -> []
+  (e:es) -> e : normalize es
+
+
+-- Similar to @normalize@. In addition to combining consecutive values of the same
+-- type it also removes the identities.
+normalizeEq :: (Eq m, Eq n, Monoid m, Monoid n) => [Either m n] -> [Either m n]
+normalizeEq es = until (all nonIdentity) reduce (normalize es)
+  where
+    reduce = normalize . filter nonIdentity
+    nonIdentity e = e /= Left mempty && e /= Right mempty
+
 -- For efficiency and simplicity, we implement it just as [Either m
 -- n]: of course, this does not preserve the invariant of strictly
 -- alternating types, but it doesn't really matter as long as we don't
 -- let anyone inspect the internal representation.
 
+-- | Universal map of the coproduct. The name @cop@ is an abbreviation
+--   for copairing. Both functions in the signature should be monoid
+--   homomorphisms. If they are general functions then the copairing may
+--   not be well defined in the sense that it may send equal elements to
+--   unequal elements. This is also the reason why @cop@ is not the
+--   @Data.Bifoldable.bifoldMap@ function even though they have the same
+--   signature.
+cop :: Monoid k => (m -> k) -> (n -> k) -> (m :+: n) -> k
+f `cop` g = foldMap (either f g) . unMCo
+
 -- | Injection from the left monoid into a coproduct.
 inL :: m -> m :+: n
 inL m = MCo [Left m]
@@ -57,17 +108,6 @@
 mappendR :: n -> m :+: n -> m :+: n
 mappendR = mappend . inR
 
-{-
-normalize :: (Monoid m, Monoid n) => m :+: n -> m :+: n
-normalize (MCo es) = MCo (normalize' es)
-  where normalize' []  = []
-        normalize' [e] = [e]
-        normalize' (Left e1:Left e2 : es) = normalize' (Left (e1 <> e2) : es)
-        normalize' (Left e1:es) = Left e1 : normalize' es
-        normalize' (Right e1:Right e2:es) = normalize' (Right (e1 <> e2) : es)
-        normalize' (Right e1:es) = Right e1 : normalize' es
--}
-
 instance Semigroup (m :+: n) where
   (MCo es1) <> (MCo es2) = MCo (es1 ++ es2)
 
@@ -79,14 +119,21 @@
 -- | @killR@ takes a value in a coproduct monoid and sends all the
 --   values from the right monoid to the identity.
 killR :: Monoid m => m :+: n -> m
-killR = mconcat . lefts . unMCo
+killR = id `cop` const mempty
 
 -- | @killL@ takes a value in a coproduct monoid and sends all the
 --   values from the left monoid to the identity.
 killL :: Monoid n => m :+: n -> n
-killL = mconcat . rights . unMCo
+killL = const mempty `cop` id
 
--- | Take a value from a coproduct monoid where the left monoid has an
+-- | The copairing of @embed@ and @inject@ homomorphisms into the
+--   semidirect product. Note that @embed@ and @inject@ are monoid
+--   homomorphisms. Therefore @untangleSemi@ is also a monoid homomorphism.
+untangleSemi :: (Action m n, Monoid m, Monoid n) => m :+: n -> Semi n m
+untangleSemi = embed `cop` inject
+
+-- | Same as @untangleSemi@ but the result is uwrapped. Concretely, given
+--   a value from a coproduct monoid where the left monoid has an
 --   action on the right, and \"untangle\" it into a pair of values.  In
 --   particular,
 --
@@ -99,12 +146,9 @@
 --   That is, before combining @n@ values, every @n@ value is acted on
 --   by all the @m@ values to its left.
 untangle :: (Action m n, Monoid m, Monoid n) => m :+: n -> (m,n)
-untangle (MCo elts) = untangle' mempty elts
-  where untangle' cur [] = cur
-        untangle' (curM, curN) (Left m : elts')  = untangle' (curM `mappend` m, curN) elts'
-        untangle' (curM, curN) (Right n : elts') = untangle' (curM, curN `mappend` act curM n) elts'
+untangle = swap . unSemi . untangleSemi
 
 -- | Coproducts act on other things by having each of the components
 --   act individually.
 instance (Action m r, Action n r) => Action (m :+: n) r where
-  act = appEndo . mconcat . map (Endo . either act act) . unMCo
+  act = appEndo . ((Endo . act) `cop` (Endo . act))
diff --git a/src/Data/Monoid/Cut.hs b/src/Data/Monoid/Cut.hs
--- a/src/Data/Monoid/Cut.hs
+++ b/src/Data/Monoid/Cut.hs
@@ -1,8 +1,12 @@
-
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveFoldable     #-}
+{-# LANGUAGE DeriveFunctor      #-}
+{-# LANGUAGE DeriveTraversable  #-}
+{-# OPTIONS_GHC -fno-warn-unused-imports       #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Cut
--- Copyright   :  (c) 2012 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2012-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
@@ -18,7 +22,10 @@
 
        ) where
 
+import Data.Data
 import Data.Semigroup
+import Data.Foldable
+import Data.Traversable
 
 infix 5 :||:
 
@@ -38,7 +45,7 @@
 --   the outermost splits and throws away everything in between.
 data Cut m = Uncut m
            | m :||: m
-  deriving (Show)
+  deriving (Data, Typeable, Show, Read, Functor, Foldable, Traversable)
 
 -- | If @m@ is a @Semigroup@, then @Cut m@ is a semigroup which
 --   contains @m@ as a sub-semigroup, but also contains elements of
@@ -53,6 +60,9 @@
   (Uncut m1)    <> (m1' :||: m2) = m1 <> m1' :||: m2
   (m1  :||: m2) <> (Uncut m2')   = m1        :||: m2 <> m2'
   (m11 :||: _)  <> (_ :||: m22)  = m11       :||: m22
+
+  stimes n (Uncut m) = Uncut (stimes n m)
+  stimes _ (m      ) = m
 
 instance (Semigroup m, Monoid m) => Monoid (Cut m) where
   mempty  = Uncut mempty
diff --git a/src/Data/Monoid/Deletable.hs b/src/Data/Monoid/Deletable.hs
--- a/src/Data/Monoid/Deletable.hs
+++ b/src/Data/Monoid/Deletable.hs
@@ -1,10 +1,12 @@
-{-# LANGUAGE DeriveFunctor
-  #-}
-
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveFoldable     #-}
+{-# LANGUAGE DeriveFunctor      #-}
+{-# LANGUAGE DeriveTraversable  #-}
+{-# OPTIONS_GHC -fno-warn-unused-imports       #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Deletable
--- Copyright   :  (c) 2011 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2011-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
@@ -22,6 +24,9 @@
 
        ) where
 
+import Data.Data
+import Data.Foldable
+import Data.Traversable
 import Data.Semigroup
 
 -- | If @m@ is a 'Monoid', then @Deletable m@ (intuitively speaking)
@@ -52,7 +57,7 @@
 --   * The remaining case is symmetric with the second.
 
 data Deletable m = Deletable Int m Int
-  deriving (Functor, Show)
+  deriving (Data, Typeable, Show, Read, Functor, Foldable, Traversable)
 
 -- | Project the wrapped value out of a `Deletable` value.
 unDelete :: Deletable m -> m
@@ -71,6 +76,13 @@
     | l1 == r2  = Deletable r1 (m1 <> m2) l2
     | l1 <  r2  = Deletable (r1 + r2 - l1) m2 l2
     | otherwise = Deletable r1 m1 (l2 + l1 - r2)
+
+  stimes n (Deletable r m l)
+    | r == l    = Deletable r (stimes n m) l
+    | l <  r    = Deletable (i*(r-l) + l) m l
+    | otherwise = Deletable r m (i*(l-r) + r)
+    where
+      i = fromIntegral n :: Int
 
 instance (Semigroup m, Monoid m) => Monoid (Deletable m) where
   mempty = Deletable 0 mempty 0
diff --git a/src/Data/Monoid/Endomorphism.hs b/src/Data/Monoid/Endomorphism.hs
--- a/src/Data/Monoid/Endomorphism.hs
+++ b/src/Data/Monoid/Endomorphism.hs
@@ -1,10 +1,11 @@
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE CPP                  #-}
+{-# LANGUAGE FlexibleContexts     #-}
+{-# LANGUAGE StandaloneDeriving   #-}
 {-# LANGUAGE UndecidableInstances #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Endomorphism
--- Copyright   :  (c) 2013 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2013-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
@@ -20,7 +21,8 @@
 import           Control.Category
 import           Data.Group
 import           Data.Groupoid
-import           Data.Semigroup
+import           Data.Monoid       (Monoid(..))
+import           Data.Semigroup    (Semigroup(..))
 import           Data.Semigroupoid
 import           Prelude           (Show)
 
@@ -30,14 +32,16 @@
 --   and the identity morphism as the identity element.
 newtype Endomorphism k a = Endomorphism {getEndomorphism :: k a a}
 
-deriving instance Show (k a a) =>  Show (Endomorphism k a)
+deriving instance Show (k a a) => Show (Endomorphism k a)
 
 instance Semigroupoid k => Semigroup (Endomorphism k a) where
   Endomorphism a <> Endomorphism b = Endomorphism (a `o` b)
 
-instance Category k => Monoid (Endomorphism k a) where
+instance (Semigroupoid k, Category k) => Monoid (Endomorphism k a) where
   mempty = Endomorphism id
+#if !MIN_VERSION_base(4,11,0)
   Endomorphism a `mappend` Endomorphism b = Endomorphism (a . b)
+#endif
 
 instance (Category k, Groupoid k) => Group (Endomorphism k a) where
   invert (Endomorphism a) = Endomorphism (inv a)
diff --git a/src/Data/Monoid/Inf.hs b/src/Data/Monoid/Inf.hs
--- a/src/Data/Monoid/Inf.hs
+++ b/src/Data/Monoid/Inf.hs
@@ -1,19 +1,23 @@
-{-# LANGUAGE DeriveFoldable    #-}
-{-# LANGUAGE DeriveFunctor     #-}
-{-# LANGUAGE DeriveTraversable #-}
-{-# LANGUAGE EmptyDataDecls    #-}
-{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveFoldable     #-}
+{-# LANGUAGE DeriveFunctor      #-}
+{-# LANGUAGE DeriveTraversable  #-}
+{-# LANGUAGE EmptyDataDecls     #-}
+{-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-unused-imports       #-}
+
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Inf
--- Copyright   :  (c) 2012 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2012-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
--- Make semigroup under 'min' or 'max' into monoids by adjoining an
+-- Make semigroups under 'min' or 'max' into monoids by adjoining an
 -- element corresponding to infinity (positive or negative,
--- respectively). These types are similar to @Option (Min a)@ and
--- @Option (Max a)@ respectively, except that the 'Ord' instance
+-- respectively). These types are similar to @Maybe (Min a)@ and
+-- @Maybe (Max a)@ respectively, except that the 'Ord' instance
 -- matches the 'Monoid' instance.
 --
 -----------------------------------------------------------------------------
@@ -28,57 +32,111 @@
        , posFinite, negFinite
        ) where
 
+import           Control.Applicative (Applicative(..), liftA2)
+import           Data.Data
 import           Data.Semigroup
-import           Prelude          hiding (maximum, minimum)
-import qualified Prelude          as P
+import           Prelude             hiding (maximum, minimum)
+import qualified Prelude             as P
 
-import           Data.Foldable    (Foldable)
-import           Data.Traversable (Traversable)
+import           Data.Foldable       (Foldable)
+import           Data.Traversable    (Traversable)
 
+-- | Type index indicating positive infinity.
 data Pos
+-- | Type index indicating negative infinity.
 data Neg
 
+-- | @Inf p a@ represents the type 'a' extended with a new "infinite"
+--   value, which is treated as either positive or negative infinity
+--   depending on the type index 'p'.  This type exists mostly for its
+--   'Ord', 'Semigroup', and 'Monoid' instances.
 data Inf p a = Infinity | Finite a
-  deriving (Eq, Show, Read, Functor, Foldable, Traversable)
+  deriving (Data, Typeable, Show, Read, Eq, Functor, Foldable,
+            Traversable)
 
+-- | The type 'a' extended with positive infinity.
 type PosInf a = Inf Pos a
+
+-- | The type 'a' extended with negative infinity.
 type NegInf a = Inf Neg a
 
+-- | Positive infinity is greater than any finite value.
 instance Ord a => Ord (Inf Pos a) where
   compare Infinity Infinity = EQ
   compare Infinity Finite{} = GT
   compare Finite{} Infinity = LT
   compare (Finite a) (Finite b) = compare a b
 
+-- | Negative infinity is less than any finite value.
 instance Ord a => Ord (Inf Neg a) where
   compare Infinity Infinity = EQ
   compare Infinity Finite{} = LT
   compare Finite{} Infinity = GT
   compare (Finite a) (Finite b) = compare a b
 
+-- | An ordered type extended with positive infinity is a semigroup
+--   under 'min'.
 instance Ord a => Semigroup (Inf Pos a) where
   (<>) = min
 
+-- | An ordered type extended with negative infinity is a semigroup
+--   under 'max'.
 instance Ord a => Semigroup (Inf Neg a) where
   (<>) = max
 
+-- | An ordered type extended with positive infinity is a monoid under
+--   'min', with positive infinity as the identity element.
 instance Ord a => Monoid (Inf Pos a) where
   mempty = Infinity
   mappend = (<>)
 
+-- | An ordered type extended with negative infinity is a monoid under
+--   'max', with negative infinity as the identity element.
 instance Ord a => Monoid (Inf Neg a) where
   mempty = Infinity
   mappend = (<>)
 
+instance Applicative (Inf p) where
+    pure = Finite
+    Infinity <*> _ = Infinity
+    _ <*> Infinity = Infinity
+    Finite f <*> Finite x = Finite $ f x
+
+instance Monad (Inf p) where
+    Infinity >>= _ = Infinity
+    Finite x >>= f = f x
+    return = pure
+
+instance Bounded a => Bounded (NegInf a) where
+    minBound = Infinity
+    maxBound = Finite maxBound
+
+instance Bounded a => Bounded (PosInf a) where
+    minBound = Finite minBound
+    maxBound = Infinity
+
+-- | Find the minimum of a list of values.  Returns positive infinity
+--   iff the list is empty.
 minimum :: Ord a => [a] -> PosInf a
 minimum xs = P.minimum (Infinity : map Finite xs)
 
+-- | Find the maximum of a list of values.  Returns negative infinity
+--   iff the list is empty.
 maximum :: Ord a => [a] -> NegInf a
 maximum xs = P.maximum (Infinity : map Finite xs)
 
+-- | Positive infinity.
 posInfty :: PosInf a
+
+-- | Negative infinity.
 negInfty :: NegInf a
+
+-- | Embed a finite value into the space of such values extended with
+--   positive infinity.
 posFinite :: a -> PosInf a
+
+-- | Embed a finite value into the space of such values extended with
+--   negative infinity.
 negFinite :: a -> NegInf a
 
 posInfty = Infinity
diff --git a/src/Data/Monoid/MList.hs b/src/Data/Monoid/MList.hs
--- a/src/Data/Monoid/MList.hs
+++ b/src/Data/Monoid/MList.hs
@@ -1,12 +1,15 @@
+{-# LANGUAGE CPP                        #-}
 {-# LANGUAGE FlexibleContexts           #-}
 {-# LANGUAGE FlexibleInstances          #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE OverlappingInstances       #-}
 {-# LANGUAGE TypeFamilies               #-}
 {-# LANGUAGE TypeOperators              #-}
 {-# LANGUAGE TypeSynonymInstances       #-}
 {-# LANGUAGE UndecidableInstances       #-}
+#if __GLASGOW_HASKELL__ < 710
+{-# LANGUAGE OverlappingInstances       #-}
+#endif
 
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
@@ -41,7 +44,6 @@
 
 import           Control.Arrow
 import           Data.Monoid.Action
-import           Data.Semigroup
 
 -- $mlist
 --
@@ -55,10 +57,10 @@
 infixr 5 :::
 infixr 5 *:
 
-type a ::: l = (Option a, l)
+type a ::: l = (Maybe a, l)
 
 (*:) :: a -> l -> a ::: l
-a *: l = (Option (Just a), l)
+a *: l = (Just a, l)
 
 -- MList -----------------------------------
 
@@ -74,7 +76,7 @@
   empty     = ()
 
 instance MList l => MList (a ::: l) where
-  empty   = (Option Nothing, empty)
+  empty   = (Nothing, empty)
 
 -- Embedding -------------------------------------------
 
@@ -86,18 +88,22 @@
 
   -- | Get the value of type @a@ from a heterogeneous list, if there
   --   is one.
-  get  :: l -> Option a
+  get  :: l -> Maybe a
 
   -- | Alter the value of type @a@ by applying the given function to it.
-  alt  :: (Option a -> Option a) -> l -> l
+  alt  :: (Maybe a -> Maybe a) -> l -> l
 
+#if __GLASGOW_HASKELL__ >= 710
+instance {-# OVERLAPPING #-} MList t => (:>:) (a ::: t) a where
+#else
 instance MList t => (:>:) (a ::: t) a where
-  inj a = (Option (Just a), empty)
+#endif
+  inj a = (Just a, empty)
   get   = fst
   alt   = first
 
 instance (t :>: a) => (:>:) (b ::: t) a where
-  inj a = (Option Nothing, inj a)
+  inj a = (Nothing, inj a)
   get   = get . snd
   alt   = second . alt
 
@@ -122,6 +128,6 @@
 instance Action (SM a) () where
   act _ _ = ()
 
-instance (Action a a', Action (SM a) l) => Action (SM a) (Option a', l) where
-  act (SM a) (Option Nothing,   l) = (Option Nothing, act (SM a) l)
-  act (SM a) (Option (Just a'), l) = (Option (Just (act a a')), act (SM a) l)
+instance (Action a a', Action (SM a) l) => Action (SM a) (Maybe a', l) where
+  act (SM a) (Nothing,   l) = (Nothing, act (SM a) l)
+  act (SM a) (Just a', l) = (Just (act a a'), act (SM a) l)
diff --git a/src/Data/Monoid/Recommend.hs b/src/Data/Monoid/Recommend.hs
--- a/src/Data/Monoid/Recommend.hs
+++ b/src/Data/Monoid/Recommend.hs
@@ -1,24 +1,38 @@
+{-# LANGUAGE CPP                #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveFoldable     #-}
+{-# LANGUAGE DeriveFunctor      #-}
+{-# LANGUAGE DeriveTraversable  #-}
+
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Recommend
--- Copyright   :  (c) 2012 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2012-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
 -- A type for representing values with an additional bit saying
 -- whether the value is \"just a recommendation\" (to be used only if
--- nothing better comes along) or a \"committment\" (to certainly be
+-- nothing better comes along) or a \"commitment\" (to certainly be
 -- used, overriding merely recommended values), along with
 -- corresponding @Semigroup@ and @Monoid@ instances.
 --
 -----------------------------------------------------------------------------
 
 module Data.Monoid.Recommend
-       ( Recommend(..), getRecommend
+       ( Recommend(..)
+       , getRecommend
        ) where
 
-import Data.Semigroup
+#if __GLASGOW_HASKELL__ < 710
+import           Data.Foldable
+import           Data.Traversable
+#endif
 
+import           Data.Data
+import           Data.Semigroup
+
 -- | A value of type @Recommend a@ consists of a value of type @a@
 --   wrapped up in one of two constructors.  The @Recommend@
 --   constructor indicates a \"non-committal recommendation\"---that
@@ -28,7 +42,7 @@
 --   overriding any @Recommend@ed values.
 data Recommend a = Recommend a
                  | Commit a
-                   deriving Show
+  deriving (Show, Read, Functor, Eq, Ord, Typeable, Data, Foldable, Traversable)
 
 -- | Extract the value of type @a@ wrapped in @Recommend a@.
 getRecommend :: Recommend a -> a
@@ -43,6 +57,9 @@
   Recommend _ <> Commit b    = Commit b
   Commit a    <> Recommend _ = Commit a
   Commit a    <> Commit b    = Commit (a <> b)
+
+  stimes n (Recommend m) = Recommend (stimes n m)
+  stimes n (Commit    m) = Commit    (stimes n m)
 
 instance (Semigroup a, Monoid a) => Monoid (Recommend a) where
   mappend = (<>)
diff --git a/src/Data/Monoid/SemiDirectProduct.hs b/src/Data/Monoid/SemiDirectProduct.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Monoid/SemiDirectProduct.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE CPP                   #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TupleSections         #-}
+
+module Data.Monoid.SemiDirectProduct
+       ( Semi, unSemi, tag, inject, untag, embed, quotient
+       ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import           Data.Monoid        (Monoid(..))
+#endif
+import           Data.Semigroup     (Semigroup(..))
+
+import           Data.Monoid.Action
+
+-- | The semi-direct product of monoids @s@ and @m@, which is a monoid
+--   when @m@ acts on @s@. Structurally, the semi-direct product is
+--   just a pair @(s,m)@.  However, the monoid instance is different.
+--   In particular, we have
+--
+-- > (s1,m1) <> (s2,m2) = (s1 <> (m1 `act` s2), m1 <> m2)
+--
+--   We think of the @m@ values as a "tag" decorating the @s@ values,
+--   which also affect the way the @s@ values combine.
+--
+--   NOTE: this is only a valid semigroup/monoid if the action of @m@
+--   on @s@ satisfies BOTH:
+--
+--     1. @act@ is a monoid/semigroup homomorphism from @m@ to @(s ->
+--       s)@, that is, @act mempty = id@ and @act (m1 <> m2) = act m1
+--       . act m2@
+--     2. @act m@ is a monoid/semigroup homomorphism for any @m@, that is,
+--       @act m mempty = mempty@ and @act m (s1 <> s2) = act m s1 <> act m s2@.
+--
+--   For a more fine-grained treatment of these ideas, see the
+--   @lr-acts@ package.
+--
+--   We call the monoid @m@ the quotient monoid and the monoid @s@ the
+--   sub-monoid of the semi-direct product. The semi-direct product
+--   @Semi s m@ is an extension of the monoid @s@ with @m@ being the
+--   quotient.
+newtype Semi s m = Semi { unSemi :: (s,m) }
+
+instance (Semigroup m, Semigroup s, Action m s) => Semigroup (Semi s m) where
+  x <> y = Semi (xs <> (xm `act` ys), xm <> ym)
+    where (xs, xm) = unSemi x
+          (ys, ym) = unSemi y
+  {-# INLINE (<>) #-}
+
+#if MIN_VERSION_base(4,8,0)
+  sconcat = foldr1 (<>)
+  {-# INLINE sconcat #-}
+#endif
+
+instance (Monoid m, Monoid s, Action m s) => Monoid (Semi s m) where
+  mempty      = Semi (mempty, mempty)
+  {-# INLINE mempty #-}
+
+#if !MIN_VERSION_base(4,11,0)
+  mappend x y = Semi (xs `mappend` (xm `act` ys), xm `mappend` ym)
+    where (xs, xm) = unSemi x
+          (ys, ym) = unSemi y
+
+  {-# INLINE mappend #-}
+#endif
+
+  mconcat     = foldr mappend mempty
+  {-# INLINE mconcat #-}
+
+-- | Tag an @s@ value with an @m@ value to create an element of the
+--   semi-direct product.
+tag :: s -> m -> Semi s m
+tag s m = Semi (s,m)
+
+-- | The injection map, /i.e./ give an @s@ value a trivial tag.
+inject :: Monoid m => s -> Semi s m
+inject = Semi . (,mempty)
+
+-- | Forget the monoidal tag.  Of course, @untag . inject = id@, and
+--   @untag (tag s m) = s@.
+untag :: Semi s m -> s
+untag = fst . unSemi
+
+-- | Embed a "tag" value as a value of type @Semi s m@.  Note that
+--
+--   @inject s <> embed m = tag s m@
+--
+--   and
+--
+--   @embed m <> inject s@ = tag (act m s) m@
+--
+--   The semi-direct product gives a split extension of @s@ by
+--   @m@. This allows us to embed @m@ into the semi-direct
+--   product. This is the embedding map. The quotient and embed maps
+--   should satisfy the equation @quotient . embed = id@.
+embed :: Monoid s => m -> Semi s m
+embed = Semi . (mempty,)
+
+-- | The quotient map, /i.e./ retrieve the monoidal tag value.
+quotient :: Semi s m -> m
+quotient = snd . unSemi
+
+
diff --git a/src/Data/Monoid/SemiDirectProduct/Strict.hs b/src/Data/Monoid/SemiDirectProduct/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Monoid/SemiDirectProduct/Strict.hs
@@ -0,0 +1,87 @@
+-- | A strict version of the semi-direct product. If a monoid m acts
+--   on s then this version of the semi-direct product is strict in
+--   the m-portion of the semi-direct product.
+
+{-# LANGUAGE CPP                   #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TupleSections         #-}
+
+module Data.Monoid.SemiDirectProduct.Strict
+       ( Semi, unSemi, tag, inject, untag, embed, quotient
+       ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import           Data.Monoid        (Monoid(..))
+#endif
+import           Data.Semigroup     (Semigroup(..))
+
+import           Data.Monoid.Action
+
+-- | The semi-direct product of monoids @s@ and @m@, which is a monoid
+--   when @m@ acts on @s@. Structurally, the semi-direct product is
+--   just a pair @(s,m)@.  However, the monoid instance is different.
+--   In particular, we have
+--
+-- > (s1,m1) <> (s2,m2) = (s1 <> (m1 `act` s2), m1 <> m2)
+--
+--   We call the monoid @m@ the quotient monoid and the monoid @s@ the
+--   sub-monoid of the semi-direct product. The semi-direct product
+--   @Semi s m@ is an extension of the monoid @s@ with @m@ being the
+--   quotient.
+data Semi s m = Semi s !m
+
+unSemi :: Semi s m -> (s,m)
+unSemi (Semi s m) = (s,m)
+
+instance (Semigroup m, Semigroup s, Action m s) => Semigroup (Semi s m) where
+  Semi xs xm <> Semi ys ym          = Semi (xs <> (xm `act` ys)) (xm <> ym)
+  {-# INLINE (<>) #-}
+
+#if MIN_VERSION_base(4,8,0)
+  sconcat                           = foldr1 (<>)
+  {-# INLINE sconcat #-}
+#endif
+
+instance (Monoid m, Monoid s, Action m s) => Monoid (Semi s m) where
+  mempty                            = Semi mempty mempty
+  {-# INLINE mempty #-}
+#if !MIN_VERSION_base(4,11,0)
+  mappend (Semi xs xm) (Semi ys ym) = Semi (xs `mappend` (xm `act` ys)) (xm `mappend` ym)
+  {-# INLINE mappend #-}
+#endif
+  mconcat                           = foldr mappend mempty
+  {-# INLINE mconcat #-}
+
+-- | Tag an @s@ value with an @m@ value to create an element of the
+--   semi-direct product.
+tag :: s -> m -> Semi s m
+tag = Semi
+
+-- | The injection map, /i.e./ give an @s@ value a trivial tag.
+inject :: Monoid m => s -> Semi s m
+inject = flip Semi mempty
+
+-- | Forget the monoidal tag.  Of course, @untag . inject = id@, and
+--   @untag (tag s m) = s@.
+untag :: Semi s m -> s
+untag (Semi s _) = s
+
+-- | Embed a "tag" value as a value of type @Semi s m@.  Note that
+--
+--   @inject s <> embed m = tag s m@
+--
+--   and
+--
+--   @embed m <> inject s@ = tag (act m s) m@
+--
+--   The semi-direct product gives a split extension of @s@ by
+--   @m@. This allows us to embed @m@ into the semi-direct
+--   product. This is the embedding map. The quotient and embed maps
+--   should satisfy the equation @quotient . embed = id@.
+embed :: Monoid s => m -> Semi s m
+embed = Semi mempty
+
+-- | The quotient map, /i.e./ retrieve the monoidal tag value.
+quotient :: Semi s m -> m
+quotient (Semi _ m) = m
diff --git a/src/Data/Monoid/Split.hs b/src/Data/Monoid/Split.hs
--- a/src/Data/Monoid/Split.hs
+++ b/src/Data/Monoid/Split.hs
@@ -1,11 +1,14 @@
-{-# LANGUAGE FlexibleInstances
-           , MultiParamTypeClasses
-  #-}
-
+{-# LANGUAGE DeriveDataTypeable    #-}
+{-# LANGUAGE DeriveFoldable        #-}
+{-# LANGUAGE DeriveFunctor         #-}
+{-# LANGUAGE DeriveTraversable     #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# OPTIONS_GHC -fno-warn-unused-imports       #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.Split
--- Copyright   :  (c) 2011 diagrams-core team (see LICENSE)
+-- Copyright   :  (c) 2011-2015 diagrams-core team (see LICENSE)
 -- License     :  BSD-style (see LICENSE)
 -- Maintainer  :  diagrams-discuss@googlegroups.com
 --
@@ -23,11 +26,16 @@
 -----------------------------------------------------------------------------
 
 module Data.Monoid.Split
-       ( Split(..), split, unsplit
+       ( Split(..)
+       , split
+       , unsplit
 
        ) where
 
+import Data.Data
+import Data.Foldable
 import Data.Semigroup
+import Data.Traversable
 
 import Data.Monoid.Action
 
@@ -48,7 +56,7 @@
 --   keeps the outermost splits and throws away everything in between.
 data Split m = M m
              | m :| m
-  deriving (Show)
+  deriving (Data, Typeable, Show, Read, Eq, Functor, Foldable, Traversable)
 
 -- | If @m@ is a @Semigroup@, then @Split m@ is a semigroup which
 --   combines values on either side of a split, keeping only the
@@ -58,6 +66,10 @@
   (M m1)       <> (m1' :| m2)  = m1 <> m1'         :| m2
   (m1  :| m2)  <> (M m2')      = m1                :| m2 <> m2'
   (m11 :| m12) <> (m21 :| m22) = m11 <> m12 <> m21 :| m22
+
+  stimes n (M m     ) = M (stimes n m)
+  stimes 1 (m       ) = m
+  stimes n (m1 :| m2) = m1 <> stimes (pred n) (m2 <> m1) :| m2
 
 instance (Semigroup m, Monoid m) => Monoid (Split m) where
   mempty  = M mempty
diff --git a/src/Data/Monoid/WithSemigroup.hs b/src/Data/Monoid/WithSemigroup.hs
--- a/src/Data/Monoid/WithSemigroup.hs
+++ b/src/Data/Monoid/WithSemigroup.hs
@@ -1,7 +1,9 @@
-{-# LANGUAGE FlexibleInstances
-           , UndecidableInstances
-  #-}
+{-# LANGUAGE ConstraintKinds      #-}
+{-# LANGUAGE CPP                  #-}
+{-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE UndecidableInstances #-}
 
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Monoid.WithSemigroup
@@ -17,16 +19,15 @@
        ( Monoid'
        ) where
 
-import Data.Semigroup
-
--- Poor man's constraint synonym.  Eventually, once it becomes
--- standard, we can make this a real constraint synonym and get rid of
--- the UndecidableInstances flag.  Better yet, hopefully the Monoid
--- class will eventually have a Semigroup superclass.
+import           Data.Semigroup
 
--- | The @Monoid'@ class is a synonym for things which are instances
---   of both 'Semigroup' and 'Monoid'.  Ideally, the 'Monoid' class
---   itself will eventually include a 'Semigroup' superclass and we
---   can get rid of this.
-class (Semigroup m, Monoid m) => Monoid' m
-instance (Semigroup m, Monoid m) => Monoid' m
+-- | For base < 4.11, the @Monoid'@ constraint is a synonym for things
+--   which are instances of both 'Semigroup' and 'Monoid'.  For base
+--   version 4.11 and onwards, @Monoid@ has @Semigroup@ as a
+--   superclass already, so for backwards compatibility @Monoid'@ is
+--   provided as a synonym for @Monoid@.
+#if MIN_VERSION_base(4,11,0)
+type Monoid' = Monoid
+#else
+type Monoid' m = (Semigroup m, Monoid m)
+#endif
diff --git a/src/Data/Semigroup/Coproduct.hs b/src/Data/Semigroup/Coproduct.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Semigroup/Coproduct.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE DeriveDataTypeable    #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE LambdaCase #-}
+
+module Data.Semigroup.Coproduct
+       ( (:+.)
+       , inL, inR
+       , cop
+       , toAltList
+       , toMonoid
+       ) where
+
+import Data.Function (on)
+import Data.List.NonEmpty (NonEmpty(..))
+import Data.Typeable (Typeable)
+import Data.Semigroup (Endo(Endo, appEndo))
+import Data.Semigroup.Foldable (foldMap1)
+
+import Data.Monoid.Action (Action(..))
+import Data.Monoid.Coproduct ((:+:))
+import qualified Data.Monoid.Coproduct as M
+
+-- | @m :+. n@ is the coproduct of semigroups @m@ and @n@.  Values of
+--   type @m :+. n@ consist of alternating non-empty lists of @m@ and @n@
+--   values. Composition is list concatenation, with appropriate
+--   combining of adjacent elements
+newtype m :+. n = SCo { unSCo :: NonEmpty (Either m n) }
+  deriving (Typeable, Show)
+
+instance (Eq m, Eq n, Semigroup m, Semigroup n) => Eq (m :+. n) where
+  (==) = (==) `on` (normalize . unSCo)
+
+-- | Extract a semigroup coproduct to a non-empty list of @Either@ values.
+--   The resulting list is guaranteed to be normalized, in the sense that
+--   it will strictly alternate between @Left@ and @Right@.
+toAltList :: (Semigroup m, Semigroup n) => (m :+. n) -> NonEmpty (Either m n)
+toAltList (SCo ms) = normalize ms
+
+-- Normalize a list of @Either@ values by combining any consecutive
+-- values of the same type.
+normalize :: (Semigroup m, Semigroup n) => NonEmpty (Either m n) -> NonEmpty (Either m n)
+normalize = \case
+  Left e1 :| Left e2 : es -> normalize (Left (e1 <> e2) :| es)
+  Right e1 :| Right e2 : es -> normalize (Right (e1 <> e2) :| es)
+  e1 :| es1 -> case es1 of
+    e2 : es2 -> (e1 :| []) <> normalize (e2 :| es2)
+    [] -> e1 :| []
+
+-- | Universal map of the coproduct. The name @cop@ is an abbreviation
+--   for copairing. Both functions in the signature should be semigroup
+--   homomorphisms. If they are general functions then the copairing may
+--   not be well defined in the sense that it may send equal elements to
+--   unequal elements. This is also the reason why @cop@ is not the
+--   @Data.Bifoldable1.bifoldMap1@ function even though they have the same
+--   signature.
+cop :: Semigroup k => (m -> k) -> (n -> k) -> (m :+. n) -> k
+f `cop` g = foldMap1 (either f g) . unSCo
+
+-- | Injection from the left semigroup into a coproduct.
+inL :: m -> m :+. n
+inL m = SCo (Left m :| [])
+
+-- | Injection from the right semigroup into a coproduct.
+inR :: n -> m :+. n
+inR n = SCo (Right n :| [])
+
+-- | Given monoids @m@ and @n@, we can form their semigroup coproduct
+--   @m :+. n@. Every monoid homomorphism is a semigroup homomorphism.
+--   In particular the canonical inections of the monoid coproduct from
+--   @m@ and @n@ into @m :+: n@ are semigroup homomorphisms. By pairing
+--   them using the universal property of the semigroup coproduct we
+--   obtain a canonical semigroup homomorphism `toMonoid` from @m :+. n@
+--   to @m :+: n@.
+toMonoid :: (Monoid m, Monoid n) => m :+. n -> m :+: n
+toMonoid = M.inL `cop` M.inR
+
+instance Semigroup (m :+. n) where
+  (SCo es1) <> (SCo es2) = SCo (es1 <> es2)
+
+-- | Coproducts act on other things by having each of the components
+--   act individually.
+instance (Action m r, Action n r) => Action (m :+. n) r where
+  act = appEndo . ((Endo . act) `cop` (Endo . act))
