diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+* 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
diff --git a/benchmarks/SemiDirectProduct.hs b/benchmarks/SemiDirectProduct.hs
new file mode 100644
--- /dev/null
+++ b/benchmarks/SemiDirectProduct.hs
@@ -0,0 +1,37 @@
+{-# 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
+
+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 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,5 +1,5 @@
 name:                monoid-extras
-version:             0.4.0.4
+version:             0.4.1
 synopsis:            Various extra monoid-related definitions and utilities
 description:         Various extra monoid-related definitions and utilities,
                      such as monoid actions, monoid coproducts, \"deletable\"
@@ -22,6 +22,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,
@@ -43,3 +45,12 @@
                      FlexibleInstances,
                      MultiParamTypeClasses,
                      TypeOperators
+
+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.10
+               , criterion
+               , monoid-extras
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,50 @@
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TupleSections         #-}
+{-# LANGUAGE CPP                   #-}
+
+module Data.Monoid.SemiDirectProduct
+       ( Semi, quotient, inject, embed
+       ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid
+#endif
+
+import Data.Monoid.Action
+
+-- | The semi-direct product of monoids @s@ and @m@. When the monoid
+-- @m@ acts on the monoid @s@, this type acquires a monoid structure.
+-- 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 (Monoid m, Monoid s, Action m s) => Monoid (Semi s m) where
+  mempty      = Semi (mempty, mempty)
+  {-# INLINE mempty #-}
+
+  mappend x y = Semi (xs `mappend` (xm `act` ys), xm `mappend` ym)
+    where (xs, xm) = unSemi x
+          (ys, ym) = unSemi y
+
+  {-# INLINE mappend #-}
+  mconcat     = foldr mappend mempty
+  {-# INLINE mconcat #-}
+
+-- | The quotient map.
+quotient :: Semi s m -> m
+quotient = snd . unSemi
+
+-- | The injection map.
+inject :: Monoid m => s -> Semi s m
+inject = Semi . (,mempty)
+
+-- | 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,)
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,50 @@
+-- | 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 FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TupleSections         #-}
+{-# LANGUAGE CPP                   #-}
+
+module Data.Monoid.SemiDirectProduct.Strict
+       ( Semi, quotient, inject, embed
+       ) where
+
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid
+#endif
+
+import Data.Monoid.Action
+
+-- | The semi-direct product of monoids @s@ and @m@. When the monoid
+-- @m@ acts on the monoid @s@, this type acquires a monoid structure.
+-- 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
+
+
+instance (Monoid m, Monoid s, Action m s) => Monoid (Semi s m) where
+  mempty                            = Semi mempty mempty
+  {-# INLINE mempty #-}
+  mappend (Semi xs xm) (Semi ys ym) = Semi (xs `mappend` (xm `act` ys)) (xm `mappend` ym)
+  {-# INLINE mappend #-}
+  mconcat                           = foldr mappend mempty
+  {-# INLINE mconcat #-}
+  
+-- | The quotient map.
+quotient :: Semi s m -> m
+quotient (Semi _ m) = m
+
+-- | The injection map.
+inject :: Monoid m => s -> Semi s m
+inject = flip Semi mempty
+
+-- | 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
