diff --git a/semiring-num.cabal b/semiring-num.cabal
--- a/semiring-num.cabal
+++ b/semiring-num.cabal
@@ -1,5 +1,5 @@
 name:                semiring-num
-version:             0.5.0.0
+version:             0.5.1.1
 synopsis:            Basic semiring class and instances
 description:         Adds a basic semiring class
 homepage:            https://github.com/oisdk/semiring-num
diff --git a/src/Data/Semiring.hs b/src/Data/Semiring.hs
--- a/src/Data/Semiring.hs
+++ b/src/Data/Semiring.hs
@@ -15,6 +15,8 @@
   ( Semiring(..)
   , Add(..)
   , Mul(..)
+  , add
+  , mul
   , Max(..)
   , Min(..)
   ) where
@@ -220,27 +222,59 @@
   (<.>) = (coerce :: WrapBinary Mul a) (<.>)
 
 ------------------------------------------------------------------------
+-- Addition and multiplication folds
+------------------------------------------------------------------------
+
+-- | Takes the sum of the elements of a 'Foldable'. Analogous to 'sum'
+-- on numbers, or 'or' on 'Bool's.
+add :: (Foldable f, Semiring a) => f a -> a
+add = getAdd . foldMap Add
+
+-- | Takes the product of the elements of a 'Foldable'. Analogous to
+-- 'product' on numbers, or 'and' on 'Bool's.
+mul :: (Foldable f, Semiring a) => f a -> a
+mul = getMul . foldMap Mul
+
+------------------------------------------------------------------------
 -- Ord wrappers
 ------------------------------------------------------------------------
 
 
 -- | The "<https://ncatlab.org/nlab/show/tropical+semiring Tropical>" or
 -- min-plus semiring. It is a semiring where:
--- @'<+>'  = 'min'@
--- @'zero' = -∞@ (represented by 'Nothing')
--- @'<.>'  = '<+>'@ (over the inner value)
--- @'one'  = 'zero'@ (over the inner value)
+--
+-- @'<+>'  = 'min'
+--'zero' = -∞ -- represented by 'Nothing'
+--'<.>'  = '<+>'
+--'one'  = 'zero'@
+--
+-- Note that we can't use 'Data.Semigroup.Min' from 'Data.Semigroup'
+-- because annihilation needs to hold:
+--
+-- @-∞ '<+>' x = x '<+>' -∞ = -∞@
+--
+-- Taking -∞ to be 'minBound' would break the above law. Using 'Nothing'
+-- to represent it follows the law.
 newtype Min a = Min
   { getMin :: Maybe a
   } deriving (Eq, Ord, Read, Show, Generic, Generic1, Functor
              ,Foldable)
 
--- | The "<https://ncatlab.org/nlab/show/https://ncatlab.org/nlab/show/max-plus+algebra Arctic>"
+-- | The "<https://ncatlab.org/nlab/show/max-plus+algebra Arctic>"
 -- or max-plus semiring. It is a semiring where:
--- @'<+>'  = 'max'@
--- @'zero' = ∞@ (represented by 'Nothing')
--- @'<.>'  = '<+>'@ (over the inner value)
--- @'one'  = 'zero'@ (over the inner value)
+--
+-- @'<+>'  = 'max'
+--'zero' = ∞ -- represented by 'Nothing'
+--'<.>'  = '<+>'
+--'one'  = 'zero'@
+--
+-- Note that we can't use 'Data.Semigroup.Max' from 'Data.Semigroup'
+-- because annihilation needs to hold:
+--
+-- @∞ '<+>' x = x '<+>' ∞ = ∞@
+--
+-- Taking ∞ to be 'maxBound' would break the above law. Using 'Nothing'
+-- to represent it follows the law.
 newtype Max a = Max
   { getMax :: Maybe a
   } deriving (Eq, Ord, Read, Show, Generic, Generic1, Functor
