diff --git a/Data/IP/RouteTable.hs b/Data/IP/RouteTable.hs
--- a/Data/IP/RouteTable.hs
+++ b/Data/IP/RouteTable.hs
@@ -20,6 +20,8 @@
   , I.lookupKeyValue
   , findMatch
   , fromList, toList
+  , foldlWithKey
+  , foldrWithKey
   ) where
 
 import Data.IP.RouteTable.Internal as I
diff --git a/Data/IP/RouteTable/Internal.hs b/Data/IP/RouteTable/Internal.hs
--- a/Data/IP/RouteTable/Internal.hs
+++ b/Data/IP/RouteTable/Internal.hs
@@ -20,7 +20,8 @@
 import Data.IP.Range
 import Data.IntMap (IntMap, (!))
 import qualified Data.IntMap as IM (fromList)
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup
 import Data.Traversable
 import Data.Word
 import GHC.Generics (Generic, Generic1)
@@ -110,12 +111,25 @@
 
 instance Foldable (IPRTable k) where
     foldMap _ Nil = mempty
-    foldMap f (Node _ _ mv b1 b2) = foldMap f mv <> foldMap f b1 <> foldMap f b2
+    foldMap f (Node _ _ mv b1 b2) = foldMap f mv `mappend` foldMap f b1 `mappend` foldMap f b2
 
 instance Traversable (IPRTable k) where
     traverse _ Nil = pure Nil
     traverse f (Node r a mv b1 b2) = Node r a <$> traverse f mv <*> traverse f b1 <*> traverse f b2
 
+-- | Note that Semigroup and Monoid instances are right-biased.
+--   That is, if both arguments have the same key, the value from the right
+--   argument will be used.
+--   Since: 1.7.5
+instance Routable k => Semigroup (IPRTable k a) where
+    a <> b = foldlWithKey (\rt k v -> insert k v rt) a b
+    stimes = stimesIdempotent
+
+-- | Since: 1.7.5
+instance Routable k => Monoid (IPRTable k a) where
+    mempty = empty
+    mappend = (<>)
+
 ----------------------------------------------------------------
 
 {-|
@@ -301,3 +315,29 @@
 foldt :: (IPRTable k a -> b -> b) -> b -> IPRTable k a -> b
 foldt _ v Nil = v
 foldt func v rt@(Node _ _ _ l r) = foldt func (foldt func (func rt v) l) r
+
+-- | /O(n)/. Fold the keys and values in the IPRTable using the given
+--   left-associative binary operator.
+--   This function is equivalent to Data.Map.foldlWithKey with necessary to
+--   IPRTable changes.
+--   Since: 1.7.5
+foldlWithKey :: (b -> AddrRange k -> a -> b) -> b -> IPRTable k a -> b
+foldlWithKey f zr = go zr
+  where
+    go z Nil = z
+    go z (Node _ _ Nothing l r) = go (go z l) r
+    go z (Node n _ (Just v) l r) = go (f (go z l) n v) r
+{-# INLINE foldlWithKey #-}
+
+-- | /O(n)/. Fold the keys and values in the IPRTable using the given
+--   right-associative binary operator.
+--   This function is equivalent to Data.Map.foldrWithKey with necessary to
+--   IPRTable changes.
+--   Since: 1.7.5
+foldrWithKey :: (AddrRange k -> a -> b -> b) -> b -> IPRTable k a -> b
+foldrWithKey f zr = go zr
+  where
+    go z Nil = z
+    go z (Node _ _ Nothing l r) = go (go z r) l
+    go z (Node n _ (Just v) l r) = go (f n v (go z r)) l
+{-# INLINE foldrWithKey #-}
diff --git a/iproute.cabal b/iproute.cabal
--- a/iproute.cabal
+++ b/iproute.cabal
@@ -1,5 +1,5 @@
 Name:                   iproute
-Version:                1.7.4
+Version:                1.7.5
 Author:                 Kazu Yamamoto <kazu@iij.ad.jp>
 Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>
 License:                BSD3
@@ -31,6 +31,8 @@
                       , byteorder
                       , containers
                       , network
+  if impl(ghc < 8.0)
+     Build-Depends:     semigroups >= 0.17
 
 Test-Suite doctest
   Type:                 exitcode-stdio-1.0
@@ -57,6 +59,8 @@
                       , containers
                       , network
                       , safe
+  if impl(ghc < 8.0)
+     Build-Depends:     semigroups >= 0.17
 
 Source-Repository head
   Type:                 git
diff --git a/test/RouteTableSpec.hs b/test/RouteTableSpec.hs
--- a/test/RouteTableSpec.hs
+++ b/test/RouteTableSpec.hs
@@ -5,12 +5,16 @@
 module RouteTableSpec where
 
 #if __GLASGOW_HASKELL__ < 709
-import Control.Applicative
+import Control.Applicative hiding (empty)
 #endif
 import Control.Monad
+import Data.Function (on)
 import Data.IP
 import Data.IP.RouteTable.Internal
+import qualified Data.Foldable as Foldable
 import Data.List (sort, nub)
+import qualified Data.List as List
+import Data.Monoid ((<>))
 import Test.Hspec
 import Test.Hspec.QuickCheck (prop)
 import Test.QuickCheck
@@ -59,6 +63,11 @@
             (fromto_ip :: [AddrRange IPv4] -> Bool)
         prop "expands as sorted"
             (fromto_ip :: [AddrRange IPv6] -> Bool)
+    describe "folds" $ do
+        prop "foldl" prop_foldl
+        prop "foldr" prop_foldr
+    describe "monoid" $ do
+        prop "monoid instance" prop_monoid
 
 sort_ip :: (Routable a, Ord a) => [AddrRange a] -> Bool
 sort_ip xs = fromList (zip xs xs) == fromList (zip xs' xs')
@@ -82,3 +91,28 @@
   where
     ordered' _ Nil = True
     ordered' k1 (Node k2 _ _ _ _) = k1 >:> k2
+
+-- Foldl and foldr properties are adapted from Data.Map tests
+prop_foldl :: Int -> [(AddrRange IPv4, Int)] -> Property
+prop_foldl n ys = length ys > 0 ==>
+  let xs = List.nubBy ((==) `on` fst) ys
+      m  = fromList xs
+  in  Foldable.foldl (+) n m == List.foldr (+) n (List.map snd xs) &&
+      Foldable.foldl (flip (:)) [] m == reverse (List.map snd (List.sort xs)) &&
+      foldlWithKey (\b _ a -> a + b) n m == List.foldr (+) n (List.map snd xs)
+
+prop_foldr :: Int -> [(AddrRange IPv4, Int)] -> Property
+prop_foldr n ys = length ys > 0 ==>
+  let xs = List.nubBy ((==) `on` fst) ys
+      m  = fromList xs
+  in  Foldable.foldr (+) n m == List.foldr (+) n (List.map snd xs) &&
+      Foldable.foldr (:) [] m == List.map snd (List.sortBy (compare `on` fst) xs) &&
+      foldrWithKey (\_ a b -> a + b) n m == List.foldr (+) n (List.map snd xs)
+
+prop_monoid :: [(AddrRange IPv4, ())] -> [(AddrRange IPv4, ())] -> Property
+prop_monoid xs ys = length xs > 0 && length ys > 0 ==>
+  let xm = fromList xs
+      ym = fromList ys
+  in empty <> xm == xm
+  && ym <> empty == ym
+  && xm <> ym == fromList (xs ++ ys)
