diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,41 @@
+## 0.2.11.0
+
+ * Add `HashMap.findWithDefault` (soft-deprecates `HashMap.lookupDefault`).
+   Thanks, Matt Renaud.
+
+ * Add `HashMap.fromListWithKey`. Thanks, Josef Svenningsson.
+
+ * Add more folding functions and use them in `Foldable` instances. Thanks,
+   David Feuer.
+
+ * Add `HashMap.!?`, a flipped version of `lookup`. Thanks, Matt Renaud.
+
+ * Add a `Bifoldable` instance for `HashMap`. Thanks, Joseph Sible.
+
+ * Add a `HasCallStack` constraint to `(!)`. Thanks, Roman Cheplyaka.
+
+### Bug fixes
+
+ * Fix a space leak affecting updates on keys with hash collisions. Thanks,
+   Neil Mitchell. ([#254])
+
+ * Get rid of some silly thunks that could be left lying around. ([#232]).
+   Thanks, David Feuer.
+
+### Other changes
+
+ * Speed up the `Hashable` instances for `HashMap` and `HashSet`. Thanks,
+   Edward Amsden.
+
+ * Remove a dependency cycle hack from the benchmark suite. Thanks,
+   Andrew Martin.
+
+ * Improve documentation. Thanks, Tristan McLeay, Li-yao Xia, Gareth Smith,
+   Simon Jakobi, Sergey Vinokurov, and likely others.
+
+[#232]: https://github.com/haskell-unordered-containers/unordered-containers/issues/232
+[#254]: https://github.com/haskell-unordered-containers/unordered-containers/issues/254
+
 ## 0.2.10.0
 
  * Add `HashMap.alterF`.
diff --git a/Data/HashMap/Array.hs b/Data/HashMap/Array.hs
--- a/Data/HashMap/Array.hs
+++ b/Data/HashMap/Array.hs
@@ -41,8 +41,11 @@
     , copyM
 
       -- * Folds
+    , foldl
     , foldl'
     , foldr
+    , foldr'
+    , foldMap
 
     , thaw
     , map
@@ -63,9 +66,9 @@
 import Control.Monad.ST (stToIO)
 
 #if __GLASGOW_HASKELL__ >= 709
-import Prelude hiding (filter, foldr, length, map, read, traverse)
+import Prelude hiding (filter, foldMap, foldr, foldl, length, map, read, traverse)
 #else
-import Prelude hiding (filter, foldr, length, map, read)
+import Prelude hiding (filter, foldr, foldl, length, map, read)
 #endif
 
 #if __GLASGOW_HASKELL__ >= 710
@@ -79,6 +82,7 @@
                  indexArray#, unsafeFreezeArray#, unsafeThawArray#,
                  MutableArray#, sizeofArray#, copyArray#, thawArray#,
                  sizeofMutableArray#, copyMutableArray#, cloneMutableArray#)
+import Data.Monoid (Monoid (..))
 #endif
 
 #if defined(ASSERTS)
@@ -418,6 +422,15 @@
             (# x #) -> go ary n (i+1) (f z x)
 {-# INLINE foldl' #-}
 
+foldr' :: (a -> b -> b) -> b -> Array a -> b
+foldr' f = \ z0 ary0 -> go ary0 (length ary0 - 1) z0
+  where
+    go !_ary (-1) z = z
+    go !ary i !z
+      | (# x #) <- index# ary i
+      = go ary (i - 1) (f x z)
+{-# INLINE foldr' #-}
+
 foldr :: (a -> b -> b) -> b -> Array a -> b
 foldr f = \ z0 ary0 -> go ary0 (length ary0) 0 z0
   where
@@ -427,6 +440,29 @@
         = case index# ary i of
             (# x #) -> f x (go ary n (i+1) z)
 {-# INLINE foldr #-}
+
+foldl :: (b -> a -> b) -> b -> Array a -> b
+foldl f = \ z0 ary0 -> go ary0 (length ary0 - 1) z0
+  where
+    go _ary (-1) z = z
+    go ary i z
+      | (# x #) <- index# ary i
+      = f (go ary (i - 1) z) x
+{-# INLINE foldl #-}
+
+-- We go to a bit of trouble here to avoid appending an extra mempty.
+-- The below implementation is by Mateusz Kowalczyk, who indicates that
+-- benchmarks show it to be faster than one that avoids lifting out
+-- lst.
+foldMap :: Monoid m => (a -> m) -> Array a -> m
+foldMap f = \ary0 -> case length ary0 of
+  0 -> mempty
+  len ->
+    let !lst = len - 1
+        go i | (# x #) <- index# ary0 i, let fx = f x =
+          if i == lst then fx else fx `mappend` go (i + 1)
+    in go 0
+{-# INLINE foldMap #-}
 
 undefinedElem :: a
 undefinedElem = error "Data.HashMap.Array: Undefined element"
diff --git a/Data/HashMap/Base.hs b/Data/HashMap/Base.hs
--- a/Data/HashMap/Base.hs
+++ b/Data/HashMap/Base.hs
@@ -25,6 +25,8 @@
     , size
     , member
     , lookup
+    , (!?)
+    , findWithDefault
     , lookupDefault
     , (!)
     , insert
@@ -56,10 +58,15 @@
     , intersectionWithKey
 
       -- * Folds
+    , foldr'
     , foldl'
+    , foldrWithKey'
     , foldlWithKey'
     , foldr
+    , foldl
     , foldrWithKey
+    , foldlWithKey
+    , foldMapWithKey
 
       -- * Filter
     , mapMaybe
@@ -75,6 +82,7 @@
     , toList
     , fromList
     , fromListWith
+    , fromListWithKey
 
       -- Internals used by the strict version
     , Hash
@@ -124,9 +132,12 @@
 import Data.Bits ((.&.), (.|.), complement, popCount)
 import Data.Data hiding (Typeable)
 import qualified Data.Foldable as Foldable
+#if MIN_VERSION_base(4,10,0)
+import Data.Bifoldable
+#endif
 import qualified Data.List as L
 import GHC.Exts ((==#), build, reallyUnsafePtrEquality#)
-import Prelude hiding (filter, foldr, lookup, map, null, pred)
+import Prelude hiding (filter, foldl, foldr, lookup, map, null, pred)
 import Text.Read hiding (step)
 
 import qualified Data.HashMap.Array as A
@@ -142,6 +153,7 @@
 
 #if MIN_VERSION_base(4,9,0)
 import Data.Functor.Classes
+import GHC.Stack
 #endif
 
 #if MIN_VERSION_hashable(1,2,5)
@@ -197,14 +209,58 @@
     fmap = map
 
 instance Foldable.Foldable (HashMap k) where
-    foldr f = foldrWithKey (const f)
+    foldMap f = foldMapWithKey (\ _k v -> f v)
+    {-# INLINE foldMap #-}
+    foldr = foldr
+    {-# INLINE foldr #-}
+    foldl = foldl
+    {-# INLINE foldl #-}
+    foldr' = foldr'
+    {-# INLINE foldr' #-}
+    foldl' = foldl'
+    {-# INLINE foldl' #-}
+#if MIN_VERSION_base(4,8,0)
+    null = null
+    {-# INLINE null #-}
+    length = size
+    {-# INLINE length #-}
+#endif
 
+#if MIN_VERSION_base(4,10,0)
+-- | @since 0.2.11
+instance Bifoldable HashMap where
+    bifoldMap f g = foldMapWithKey (\ k v -> f k `mappend` g v)
+    {-# INLINE bifoldMap #-}
+    bifoldr f g = foldrWithKey (\ k v acc -> k `f` (v `g` acc))
+    {-# INLINE bifoldr #-}
+    bifoldl f g = foldlWithKey (\ acc k v -> (acc `f` k) `g` v)
+    {-# INLINE bifoldl #-}
+#endif
+
 #if __GLASGOW_HASKELL__ >= 711
+-- | '<>' = 'union'
+--
+-- If a key occurs in both maps, the mapping from the first will be the mapping in the result.
+--
+-- ==== __Examples__
+--
+-- >>> fromList [(1,'a'),(2,'b')] <> fromList [(2,'c'),(3,'d')]
+-- fromList [(1,'a'),(2,'b'),(3,'d')]
 instance (Eq k, Hashable k) => Semigroup (HashMap k v) where
   (<>) = union
   {-# INLINE (<>) #-}
 #endif
 
+-- | 'mempty' = 'empty'
+--
+-- 'mappend' = 'union'
+--
+-- If a key occurs in both maps, the mapping from the first will be the mapping in the result.
+--
+-- ==== __Examples__
+--
+-- >>> mappend (fromList [(1,'a'),(2,'b')]) (fromList [(2,'c'),(3,'d')])
+-- fromList [(1,'a'),(2,'b'),(3,'d')]
 instance (Eq k, Hashable k) => Monoid (HashMap k v) where
   mempty = empty
   {-# INLINE mempty #-}
@@ -277,6 +333,24 @@
     liftEq = equal1
 #endif
 
+-- | Note that, in the presence of hash collisions, equal @HashMap@s may
+-- behave differently, i.e. substitutivity may be violated:
+--
+-- >>> data D = A | B deriving (Eq, Show)
+-- >>> instance Hashable D where hashWithSalt salt _d = salt
+--
+-- >>> x = fromList [(A,1), (B,2)]
+-- >>> y = fromList [(B,2), (A,1)]
+--
+-- >>> x == y
+-- True
+-- >>> toList x
+-- [(A,1),(B,2)]
+-- >>> toList y
+-- [(B,2),(A,1)]
+--
+-- In general, the lack of substitutivity can be observed with any function
+-- that depends on the key ordering, such as folds and traversals.
 instance (Eq k, Eq v) => Eq (HashMap k v) where
     (==) = equal1 (==)
 
@@ -329,11 +403,9 @@
     liftCompare = cmp compare
 #endif
 
--- | The order is total.
---
--- /Note:/ Because the hash is not guaranteed to be stable across library
--- versions, OSes, or architectures, neither is an actual order of elements in
--- 'HashMap' or an result of `compare`.is stable.
+-- | The ordering is total and consistent with the `Eq` instance. However,
+-- nothing else about the ordering is specified, and it may change from 
+-- version to version of either this package or of hashable.
 instance (Ord k, Ord v) => Ord (HashMap k v) where
     compare = cmp compare compare
 
@@ -355,7 +427,7 @@
     go [] [] = EQ
     go [] _  = LT
     go _  [] = GT
-    go _ _ = error "cmp: Should never happend, toList' includes non Leaf / Collision"
+    go _ _ = error "cmp: Should never happen, toList' includes non Leaf / Collision"
 
     leafCompare (L k v) (L k' v') = cmpk k k' `mappend` cmpv v v'
 
@@ -420,17 +492,18 @@
 #endif
 
 instance (Hashable k, Hashable v) => Hashable (HashMap k v) where
-    hashWithSalt salt hm = go salt (toList' hm [])
+    hashWithSalt salt hm = go salt hm
       where
-        go :: Int -> [HashMap k v] -> Int
-        go s [] = s
-        go s (Leaf _ l : tl)
-          = s `hashLeafWithSalt` l `go` tl
+        go :: Int -> HashMap k v -> Int
+        go s Empty = s
+        go s (BitmapIndexed _ a) = A.foldl' go s a
+        go s (Leaf h (L _ v))
+          = s `H.hashWithSalt` h `H.hashWithSalt` v
         -- For collisions we hashmix hash value
         -- and then array of values' hashes sorted
-        go s (Collision h a : tl)
-          = (s `H.hashWithSalt` h) `hashCollisionWithSalt` a `go` tl
-        go s (_ : tl) = s `go` tl
+        go s (Full a) = A.foldl' go s a
+        go s (Collision h a)
+          = (s `H.hashWithSalt` h) `hashCollisionWithSalt` a
 
         hashLeafWithSalt :: Int -> Leaf k v -> Int
         hashLeafWithSalt s (L k v) = s `H.hashWithSalt` k `H.hashWithSalt` v
@@ -614,18 +687,47 @@
 {-# INLINE lookupCont #-}
 
 -- | /O(log n)/ Return the value to which the specified key is mapped,
+-- or 'Nothing' if this map contains no mapping for the key.
+--
+-- This is a flipped version of 'lookup'.
+--
+-- @since 0.2.11
+(!?) :: (Eq k, Hashable k) => HashMap k v -> k -> Maybe v
+(!?) m k = lookup k m
+{-# INLINE (!?) #-}
+
+
+-- | /O(log n)/ Return the value to which the specified key is mapped,
 -- or the default value if this map contains no mapping for the key.
-lookupDefault :: (Eq k, Hashable k)
+--
+-- @since 0.2.11
+findWithDefault :: (Eq k, Hashable k)
               => v          -- ^ Default value to return.
               -> k -> HashMap k v -> v
-lookupDefault def k t = case lookup k t of
+findWithDefault def k t = case lookup k t of
     Just v -> v
     _      -> def
-{-# INLINABLE lookupDefault #-}
+{-# INLINABLE findWithDefault #-}
 
+
+-- | /O(log n)/ Return the value to which the specified key is mapped,
+-- or the default value if this map contains no mapping for the key.
+--
+-- DEPRECATED: lookupDefault is deprecated as of version 0.2.10, replaced
+-- by 'findWithDefault'.
+lookupDefault :: (Eq k, Hashable k)
+              => v          -- ^ Default value to return.
+              -> k -> HashMap k v -> v
+lookupDefault def k t = findWithDefault def k t
+{-# INLINE lookupDefault #-}
+
 -- | /O(log n)/ Return the value to which the specified key is mapped.
 -- Calls 'error' if this map contains no mapping for the key.
+#if MIN_VERSION_base(4,9,0)
+(!) :: (Eq k, Hashable k, HasCallStack) => HashMap k v -> k -> v
+#else
 (!) :: (Eq k, Hashable k) => HashMap k v -> k -> v
+#endif
 (!) m k = case lookup k m of
     Just v  -> v
     Nothing -> error "Data.HashMap.Base.(!): key not found"
@@ -666,7 +768,7 @@
                          then t
                          else Leaf h (L k x)
                     else collision h l (L k x)
-        | otherwise = runST (two s h k x hy ky y)
+        | otherwise = runST (two s h k x hy t)
     go h k x s t@(BitmapIndexed b ary)
         | b .&. m == 0 =
             let !ary' = A.insert ary i $! Leaf h (L k x)
@@ -687,7 +789,7 @@
             else Full (update16 ary i st')
       where i = index h s
     go h k x s t@(Collision hy v)
-        | h == hy   = Collision h (updateOrSnocWith const k x v)
+        | h == hy   = Collision h (updateOrSnocWith (\a _ -> (# a #)) k x v)
         | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t)
 {-# INLINABLE insert' #-}
 
@@ -702,9 +804,9 @@
 insertNewKey !h0 !k0 x0 !m0 = go h0 k0 x0 0 m0
   where
     go !h !k x !_ Empty = Leaf h (L k x)
-    go h k x s (Leaf hy l@(L ky y))
+    go h k x s t@(Leaf hy l)
       | hy == h = collision h l (L k x)
-      | otherwise = runST (two s h k x hy ky y)
+      | otherwise = runST (two s h k x hy t)
     go h k x s (BitmapIndexed b ary)
         | b .&. m == 0 =
             let !ary' = A.insert ary i $! Leaf h (L k x)
@@ -791,7 +893,7 @@
                          then return t
                          else return $! Leaf h (L k x)
                     else return $! collision h l (L k x)
-        | otherwise = two s h k x hy ky y
+        | otherwise = two s h k x hy t
     go h k x s t@(BitmapIndexed b ary)
         | b .&. m == 0 = do
             ary' <- A.insertM ary i $! Leaf h (L k x)
@@ -810,24 +912,31 @@
         return t
       where i = index h s
     go h k x s t@(Collision hy v)
-        | h == hy   = return $! Collision h (updateOrSnocWith const k x v)
+        | h == hy   = return $! Collision h (updateOrSnocWith (\a _ -> (# a #)) k x v)
         | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t)
 {-# INLINABLE unsafeInsert #-}
 
--- | Create a map from two key-value pairs which hashes don't collide.
-two :: Shift -> Hash -> k -> v -> Hash -> k -> v -> ST s (HashMap k v)
+-- | Create a map from two key-value pairs which hashes don't collide. To
+-- enhance sharing, the second key-value pair is represented by the hash of its
+-- key and a singleton HashMap pairing its key with its value.
+--
+-- Note: to avoid silly thunks, this function must be strict in the
+-- key. See issue #232. We don't need to force the HashMap argument
+-- because it's already in WHNF (having just been matched) and we
+-- just put it directly in an array.
+two :: Shift -> Hash -> k -> v -> Hash -> HashMap k v -> ST s (HashMap k v)
 two = go
   where
-    go s h1 k1 v1 h2 k2 v2
+    go s h1 k1 v1 h2 t2
         | bp1 == bp2 = do
-            st <- go (s+bitsPerSubkey) h1 k1 v1 h2 k2 v2
+            st <- go (s+bitsPerSubkey) h1 k1 v1 h2 t2
             ary <- A.singletonM st
-            return $! BitmapIndexed bp1 ary
+            return $ BitmapIndexed bp1 ary
         | otherwise  = do
-            mary <- A.new 2 $ Leaf h1 (L k1 v1)
-            A.write mary idx2 $ Leaf h2 (L k2 v2)
+            mary <- A.new 2 $! Leaf h1 (L k1 v1)
+            A.write mary idx2 t2
             ary <- A.unsafeFreeze mary
-            return $! BitmapIndexed (bp1 .|. bp2) ary
+            return $ BitmapIndexed (bp1 .|. bp2) ary
       where
         bp1  = mask h1 s
         bp2  = mask h2 s
@@ -866,7 +975,7 @@
                       (# v' #) | ptrEq y v' -> t
                                | otherwise -> Leaf h (L k (v'))
                     else collision h l (L k x)
-        | otherwise = runST (two s h k x hy ky y)
+        | otherwise = runST (two s h k x hy t)
     go h k s t@(BitmapIndexed b ary)
         | b .&. m == 0 =
             let ary' = A.insert ary i $! Leaf h (L k x)
@@ -921,16 +1030,22 @@
 unsafeInsertWith :: forall k v. (Eq k, Hashable k)
                  => (v -> v -> v) -> k -> v -> HashMap k v
                  -> HashMap k v
-unsafeInsertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0)
+unsafeInsertWith f k0 v0 m0 = unsafeInsertWithKey (const f) k0 v0 m0
+{-# INLINABLE unsafeInsertWith #-}
+
+unsafeInsertWithKey :: forall k v. (Eq k, Hashable k)
+                 => (k -> v -> v -> v) -> k -> v -> HashMap k v
+                 -> HashMap k v
+unsafeInsertWithKey f k0 v0 m0 = runST (go h0 k0 v0 0 m0)
   where
     h0 = hash k0
     go :: Hash -> k -> v -> Shift -> HashMap k v -> ST s (HashMap k v)
     go !h !k x !_ Empty = return $! Leaf h (L k x)
-    go h k x s (Leaf hy l@(L ky y))
+    go h k x s t@(Leaf hy l@(L ky y))
         | hy == h = if ky == k
-                    then return $! Leaf h (L k (f x y))
+                    then return $! Leaf h (L k (f k x y))
                     else return $! collision h l (L k x)
-        | otherwise = two s h k x hy ky y
+        | otherwise = two s h k x hy t
     go h k x s t@(BitmapIndexed b ary)
         | b .&. m == 0 = do
             ary' <- A.insertM ary i $! Leaf h (L k x)
@@ -949,9 +1064,9 @@
         return t
       where i = index h s
     go h k x s t@(Collision hy v)
-        | h == hy   = return $! Collision h (updateOrSnocWith f k x v)
+        | h == hy   = return $! Collision h (updateOrSnocWithKey (\key a b -> (# f key a b #) ) k x v)
         | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t)
-{-# INLINABLE unsafeInsertWith #-}
+{-# INLINABLE unsafeInsertWithKey #-}
 
 -- | /O(log n)/ Remove the mapping for the specified key from this map
 -- if present.
@@ -1110,9 +1225,9 @@
         | otherwise = t
 {-# INLINABLE adjust# #-}
 
--- | /O(log n)/  The expression (@'update' f k map@) updates the value @x@ at @k@,
--- (if it is in the map). If (f k x) is @'Nothing', the element is deleted.
--- If it is (@'Just' y), the key k is bound to the new value y.
+-- | /O(log n)/  The expression @('update' f k map)@ updates the value @x@ at @k@
+-- (if it is in the map). If @(f x)@ is 'Nothing', the element is deleted.
+-- If it is @('Just' y)@, the key @k@ is bound to the new value @y@.
 update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> HashMap k a -> HashMap k a
 update f = alter (>>= f)
 {-# INLINABLE update #-}
@@ -1136,7 +1251,7 @@
 -- Note: 'alterF' is a flipped version of the 'at' combinator from
 -- <https://hackage.haskell.org/package/lens-4.15.4/docs/Control-Lens-At.html#v:at Control.Lens.At>.
 --
--- @since 0.2.9
+-- @since 0.2.10
 alterF :: (Functor f, Eq k, Hashable k)
        => (Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
 -- We only calculate the hash once, but unless this is rewritten
@@ -1283,6 +1398,11 @@
 
 -- | /O(n+m)/ The union of two maps. If a key occurs in both maps, the
 -- mapping from the first will be the mapping in the result.
+--
+-- ==== __Examples__
+--
+-- >>> union (fromList [(1,'a'),(2,'b')]) (fromList [(2,'c'),(3,'d')])
+-- fromList [(1,'a'),(2,'b'),(3,'d')]
 union :: (Eq k, Hashable k) => HashMap k v -> HashMap k v -> HashMap k v
 union = unionWith const
 {-# INLINABLE union #-}
@@ -1312,10 +1432,10 @@
                       else collision h1 l1 l2
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Leaf h1 (L k1 v1)) t2@(Collision h2 ls2)
-        | h1 == h2  = Collision h1 (updateOrSnocWithKey f k1 v1 ls2)
+        | h1 == h2  = Collision h1 (updateOrSnocWithKey (\k a b -> (# f k a b #)) k1 v1 ls2)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Collision h1 ls1) t2@(Leaf h2 (L k2 v2))
-        | h1 == h2  = Collision h1 (updateOrSnocWithKey (flip . f) k2 v2 ls1)
+        | h1 == h2  = Collision h1 (updateOrSnocWithKey (\k a b -> (# f k b a #)) k2 v2 ls1)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Collision h1 ls1) t2@(Collision h2 ls2)
         | h1 == h2  = Collision h1 (updateOrConcatWithKey f ls1 ls2)
@@ -1500,7 +1620,7 @@
                  _      -> m
 {-# INLINABLE intersection #-}
 
--- | /O(n+m)/ Intersection of two maps. If a key occurs in both maps
+-- | /O(n*log m)/ Intersection of two maps. If a key occurs in both maps
 -- the provided function is used to combine the values from the two
 -- maps.
 intersectionWith :: (Eq k, Hashable k) => (v1 -> v2 -> v3) -> HashMap k v1
@@ -1512,7 +1632,7 @@
                  _      -> m
 {-# INLINABLE intersectionWith #-}
 
--- | /O(n+m)/ Intersection of two maps. If a key occurs in both maps
+-- | /O(n*log m)/ Intersection of two maps. If a key occurs in both maps
 -- the provided function is used to combine the values from the two
 -- maps.
 intersectionWithKey :: (Eq k, Hashable k) => (k -> v1 -> v2 -> v3)
@@ -1530,7 +1650,7 @@
 -- | /O(n)/ Reduce this map by applying a binary operator to all
 -- elements, using the given starting value (typically the
 -- left-identity of the operator).  Each application of the operator
--- is evaluated before using the result in the next application. 
+-- is evaluated before using the result in the next application.
 -- This function is strict in the starting value.
 foldl' :: (a -> v -> a) -> a -> HashMap k v -> a
 foldl' f = foldlWithKey' (\ z _ v -> f z v)
@@ -1538,8 +1658,17 @@
 
 -- | /O(n)/ Reduce this map by applying a binary operator to all
 -- elements, using the given starting value (typically the
+-- right-identity of the operator).  Each application of the operator
+-- is evaluated before using the result in the next application.
+-- This function is strict in the starting value.
+foldr' :: (v -> a -> a) -> a -> HashMap k v -> a
+foldr' f = foldrWithKey' (\ _ v z -> f v z)
+{-# INLINE foldr' #-}
+
+-- | /O(n)/ Reduce this map by applying a binary operator to all
+-- elements, using the given starting value (typically the
 -- left-identity of the operator).  Each application of the operator
--- is evaluated before using the result in the next application.  
+-- is evaluated before using the result in the next application.
 -- This function is strict in the starting value.
 foldlWithKey' :: (a -> k -> v -> a) -> a -> HashMap k v -> a
 foldlWithKey' f = go
@@ -1553,6 +1682,21 @@
 
 -- | /O(n)/ Reduce this map by applying a binary operator to all
 -- elements, using the given starting value (typically the
+-- right-identity of the operator).  Each application of the operator
+-- is evaluated before using the result in the next application.
+-- This function is strict in the starting value.
+foldrWithKey' :: (k -> v -> a -> a) -> a -> HashMap k v -> a
+foldrWithKey' f = flip go
+  where
+    go Empty z                 = z
+    go (Leaf _ (L k v)) !z     = f k v z
+    go (BitmapIndexed _ ary) !z = A.foldr' go z ary
+    go (Full ary) !z           = A.foldr' go z ary
+    go (Collision _ ary) !z    = A.foldr' (\ (L k v) z' -> f k v z') z ary
+{-# INLINE foldrWithKey' #-}
+
+-- | /O(n)/ Reduce this map by applying a binary operator to all
+-- elements, using the given starting value (typically the
 -- right-identity of the operator).
 foldr :: (v -> a -> a) -> a -> HashMap k v -> a
 foldr f = foldrWithKey (const f)
@@ -1560,17 +1704,49 @@
 
 -- | /O(n)/ Reduce this map by applying a binary operator to all
 -- elements, using the given starting value (typically the
+-- left-identity of the operator).
+foldl :: (a -> v -> a) -> a -> HashMap k v -> a
+foldl f = foldlWithKey (\a _k v -> f a v)
+{-# INLINE foldl #-}
+
+-- | /O(n)/ Reduce this map by applying a binary operator to all
+-- elements, using the given starting value (typically the
 -- right-identity of the operator).
 foldrWithKey :: (k -> v -> a -> a) -> a -> HashMap k v -> a
-foldrWithKey f = go
+foldrWithKey f = flip go
   where
-    go z Empty                 = z
-    go z (Leaf _ (L k v))      = f k v z
-    go z (BitmapIndexed _ ary) = A.foldr (flip go) z ary
-    go z (Full ary)            = A.foldr (flip go) z ary
-    go z (Collision _ ary)     = A.foldr (\ (L k v) z' -> f k v z') z ary
+    go Empty z                 = z
+    go (Leaf _ (L k v)) z      = f k v z
+    go (BitmapIndexed _ ary) z = A.foldr go z ary
+    go (Full ary) z            = A.foldr go z ary
+    go (Collision _ ary) z     = A.foldr (\ (L k v) z' -> f k v z') z ary
 {-# INLINE foldrWithKey #-}
 
+-- | /O(n)/ Reduce this map by applying a binary operator to all
+-- elements, using the given starting value (typically the
+-- left-identity of the operator).
+foldlWithKey :: (a -> k -> v -> a) -> a -> HashMap k v -> a
+foldlWithKey f = go
+  where
+    go z Empty                 = z
+    go z (Leaf _ (L k v))      = f z k v
+    go z (BitmapIndexed _ ary) = A.foldl go z ary
+    go z (Full ary)            = A.foldl go z ary
+    go z (Collision _ ary)     = A.foldl (\ z' (L k v) -> f z' k v) z ary
+{-# INLINE foldlWithKey #-}
+
+-- | /O(n)/ Reduce the map by applying a function to each element
+-- and combining the results with a monoid operation.
+foldMapWithKey :: Monoid m => (k -> v -> m) -> HashMap k v -> m
+foldMapWithKey f = go
+  where
+    go Empty = mempty
+    go (Leaf _ (L k v)) = f k v
+    go (BitmapIndexed _ ary) = A.foldMap go ary
+    go (Full ary) = A.foldMap go ary
+    go (Collision _ ary) = A.foldMap (\ (L k v) -> f k v) ary
+{-# INLINE foldMapWithKey #-}
+
 ------------------------------------------------------------------------
 -- * Filter
 
@@ -1712,11 +1888,69 @@
 {-# INLINABLE fromList #-}
 
 -- | /O(n*log n)/ Construct a map from a list of elements.  Uses
--- the provided function to merge duplicate entries.
+-- the provided function @f@ to merge duplicate entries with
+-- @(f newVal oldVal)@.
+--
+-- === Examples
+--
+-- Given a list @xs@, create a map with the number of occurrences of each
+-- element in @xs@:
+--
+-- > let xs = ['a', 'b', 'a']
+-- > in fromListWith (+) [ (x, 1) | x <- xs ]
+-- >
+-- > = fromList [('a', 2), ('b', 1)]
+--
+-- Given a list of key-value pairs @xs :: [(k, v)]@, group all values by their
+-- keys and return a @HashMap k [v]@.
+--
+-- > let xs = [('a', 1), ('b', 2), ('a', 3)]
+-- > in fromListWith (++) [ (k, [v]) | (k, v) <- xs ]
+-- >
+-- > = fromList [('a', [3, 1]), ('b', [2])]
+--
+-- Note that the lists in the resulting map contain elements in reverse order
+-- from their occurences in the original list.
+--
+-- More generally, duplicate entries are accumulated as follows;
+-- this matters when @f@ is not commutative or not associative.
+--
+-- > fromListWith f [(k, a), (k, b), (k, c), (k, d)]
+-- > = fromList [(k, f d (f c (f b a)))]
 fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v
 fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty
 {-# INLINE fromListWith #-}
 
+-- | /O(n*log n)/ Construct a map from a list of elements.  Uses
+-- the provided function to merge duplicate entries.
+--
+-- === Examples
+--
+-- Given a list of key-value pairs where the keys are of different flavours, e.g:
+--
+-- > data Key = Div | Sub
+--
+-- and the values need to be combined differently when there are duplicates,
+-- depending on the key:
+--
+-- > combine Div = div
+-- > combine Sub = (-)
+--
+-- then @fromListWithKey@ can be used as follows:
+--
+-- > fromListWithKey combine [(Div, 2), (Div, 6), (Sub, 2), (Sub, 3)]
+-- > = fromList [(Div, 3), (Sub, 1)]
+--
+-- More generally, duplicate entries are accumulated as follows;
+--
+-- > fromListWith f [(k, a), (k, b), (k, c), (k, d)]
+-- > = fromList [(k, f k d (f k c (f k b a)))]
+--
+-- @since 0.2.11
+fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
+fromListWithKey f = L.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty
+{-# INLINE fromListWithKey #-}
+
 ------------------------------------------------------------------------
 -- Array operations
 
@@ -1766,12 +2000,12 @@
                      | otherwise -> go k ary (i+1) n
 {-# INLINABLE updateWith# #-}
 
-updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.Array (Leaf k v)
+updateOrSnocWith :: Eq k => (v -> v -> (# v #)) -> k -> v -> A.Array (Leaf k v)
                  -> A.Array (Leaf k v)
 updateOrSnocWith f = updateOrSnocWithKey (const f)
 {-# INLINABLE updateOrSnocWith #-}
 
-updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.Array (Leaf k v)
+updateOrSnocWithKey :: Eq k => (k -> v -> v -> (# v #)) -> k -> v -> A.Array (Leaf k v)
                  -> A.Array (Leaf k v)
 updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0)
   where
@@ -1782,9 +2016,12 @@
             A.copy ary 0 mary 0 n
             A.write mary n (L k v)
             return mary
-        | otherwise = case A.index ary i of
-            (L kx y) | k == kx   -> A.update ary i (L k (f k v y))
-                     | otherwise -> go k v ary (i+1) n
+        | L kx y <- A.index ary i
+        , k == kx
+        , (# v2 #) <- f k v y
+            = A.update ary i (L k v2)
+        | otherwise
+            = go k v ary (i+1) n
 {-# INLINABLE updateOrSnocWithKey #-}
 
 updateOrConcatWith :: Eq k => (v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v)
diff --git a/Data/HashMap/Lazy.hs b/Data/HashMap/Lazy.hs
--- a/Data/HashMap/Lazy.hs
+++ b/Data/HashMap/Lazy.hs
@@ -38,6 +38,8 @@
     , size
     , member
     , lookup
+    , (!?)
+    , findWithDefault
     , lookupDefault
     , (!)
     , insert
@@ -68,10 +70,15 @@
     , intersectionWithKey
 
       -- * Folds
+    , foldMapWithKey
+    , foldr
+    , foldl
+    , foldr'
     , foldl'
+    , foldrWithKey'
     , foldlWithKey'
-    , foldr
     , foldrWithKey
+    , foldlWithKey
 
       -- * Filter
     , filter
@@ -87,6 +94,7 @@
     , toList
     , fromList
     , fromListWith
+    , fromListWithKey
 
       -- ** HashSets
     , HS.keysSet
@@ -100,4 +108,4 @@
 --
 -- This module satisfies the following strictness property:
 --
--- * Key arguments are evaluated to WHNF
+-- * Key arguments are evaluated to WHNF.
diff --git a/Data/HashMap/Strict.hs b/Data/HashMap/Strict.hs
--- a/Data/HashMap/Strict.hs
+++ b/Data/HashMap/Strict.hs
@@ -37,6 +37,8 @@
     , size
     , member
     , lookup
+    , (!?)
+    , findWithDefault
     , lookupDefault
     , (!)
     , insert
@@ -67,10 +69,15 @@
     , intersectionWithKey
 
       -- * Folds
+    , foldMapWithKey
+    , foldr
+    , foldl
+    , foldr'
     , foldl'
+    , foldrWithKey'
     , foldlWithKey'
-    , foldr
     , foldrWithKey
+    , foldlWithKey
 
       -- * Filter
     , filter
@@ -86,6 +93,7 @@
     , toList
     , fromList
     , fromListWith
+    , fromListWithKey
 
       -- ** HashSets
     , HS.keysSet
@@ -94,3 +102,12 @@
 import Data.HashMap.Strict.Base as HM
 import qualified Data.HashSet.Base as HS
 import Prelude ()
+
+-- $strictness
+--
+-- This module satisfies the following strictness properties:
+--
+-- 1. Key arguments are evaluated to WHNF;
+--
+-- 2. Keys and values are evaluated to WHNF before they are stored in
+--    the map.
diff --git a/Data/HashMap/Strict/Base.hs b/Data/HashMap/Strict/Base.hs
--- a/Data/HashMap/Strict/Base.hs
+++ b/Data/HashMap/Strict/Base.hs
@@ -39,6 +39,8 @@
     , size
     , HM.member
     , HM.lookup
+    , (HM.!?)
+    , HM.findWithDefault
     , lookupDefault
     , (!)
     , insert
@@ -69,10 +71,15 @@
     , intersectionWithKey
 
       -- * Folds
+    , foldMapWithKey
+    , foldr'
     , foldl'
+    , foldrWithKey'
     , foldlWithKey'
     , HM.foldr
+    , HM.foldl
     , foldrWithKey
+    , foldlWithKey
 
       -- * Filter
     , HM.filter
@@ -88,6 +95,7 @@
     , toList
     , fromList
     , fromListWith
+    , fromListWithKey
     ) where
 
 import Data.Bits ((.&.), (.|.))
@@ -102,7 +110,8 @@
 import qualified Data.HashMap.Array as A
 import qualified Data.HashMap.Base as HM
 import Data.HashMap.Base hiding (
-    alter, alterF, adjust, fromList, fromListWith, insert, insertWith,
+    alter, alterF, adjust, fromList, fromListWith, fromListWithKey,
+    insert, insertWith,
     differenceWith, intersectionWith, intersectionWithKey, map, mapWithKey,
     mapMaybe, mapMaybeWithKey, singleton, update, unionWith, unionWithKey,
     traverseWithKey)
@@ -152,11 +161,11 @@
   where
     h0 = hash k0
     go !h !k x !_ Empty = leaf h k x
-    go h k x s (Leaf hy l@(L ky y))
+    go h k x s t@(Leaf hy l@(L ky y))
         | hy == h = if ky == k
                     then leaf h k (f x y)
                     else x `seq` (collision h l (L k x))
-        | otherwise = x `seq` runST (two s h k x hy ky y)
+        | otherwise = x `seq` runST (two s h k x hy t)
     go h k x s (BitmapIndexed b ary)
         | b .&. m == 0 =
             let ary' = A.insert ary i $! leaf h k x
@@ -182,17 +191,22 @@
 -- | In-place update version of insertWith
 unsafeInsertWith :: (Eq k, Hashable k) => (v -> v -> v) -> k -> v -> HashMap k v
                  -> HashMap k v
-unsafeInsertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0)
+unsafeInsertWith f k0 v0 m0 = unsafeInsertWithKey (const f) k0 v0 m0
+{-# INLINABLE unsafeInsertWith #-}
+
+unsafeInsertWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> k -> v -> HashMap k v
+                    -> HashMap k v
+unsafeInsertWithKey f k0 v0 m0 = runST (go h0 k0 v0 0 m0)
   where
     h0 = hash k0
     go !h !k x !_ Empty = return $! leaf h k x
-    go h k x s (Leaf hy l@(L ky y))
+    go h k x s t@(Leaf hy l@(L ky y))
         | hy == h = if ky == k
-                    then return $! leaf h k (f x y)
+                    then return $! leaf h k (f k x y)
                     else do
                         let l' = x `seq` (L k x)
                         return $! collision h l l'
-        | otherwise = x `seq` two s h k x hy ky y
+        | otherwise = x `seq` two s h k x hy t
     go h k x s t@(BitmapIndexed b ary)
         | b .&. m == 0 = do
             ary' <- A.insertM ary i $! leaf h k x
@@ -211,9 +225,9 @@
         return t
       where i = index h s
     go h k x s t@(Collision hy v)
-        | h == hy   = return $! Collision h (updateOrSnocWith f k x v)
+        | h == hy   = return $! Collision h (updateOrSnocWithKey f k x v)
         | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t)
-{-# INLINABLE unsafeInsertWith #-}
+{-# INLINABLE unsafeInsertWithKey #-}
 
 -- | /O(log n)/ Adjust the value tied to a given key in this map only
 -- if it is present. Otherwise, leave the map alone.
@@ -244,9 +258,9 @@
         | otherwise = t
 {-# INLINABLE adjust #-}
 
--- | /O(log n)/  The expression (@'update' f k map@) updates the value @x@ at @k@,
--- (if it is in the map). If (f k x) is @'Nothing', the element is deleted.
--- If it is (@'Just' y), the key k is bound to the new value y.
+-- | /O(log n)/  The expression @('update' f k map)@ updates the value @x@ at @k@
+-- (if it is in the map). If @(f x)@ is 'Nothing', the element is deleted.
+-- If it is @('Just' y)@, the key @k@ is bound to the new value @y@.
 update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> HashMap k a -> HashMap k a
 update f = alter (>>= f)
 {-# INLINABLE update #-}
@@ -268,7 +282,7 @@
 -- Note: 'alterF' is a flipped version of the 'at' combinator from
 -- <https://hackage.haskell.org/package/lens-4.15.4/docs/Control-Lens-At.html#v:at Control.Lens.At>.
 --
--- @since 0.2.9
+-- @since 0.2.10
 alterF :: (Functor f, Eq k, Hashable k)
        => (Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
 -- Special care is taken to only calculate the hash once. When we rewrite
@@ -599,21 +613,68 @@
 {-# INLINABLE fromList #-}
 
 -- | /O(n*log n)/ Construct a map from a list of elements.  Uses
--- the provided function f to merge duplicate entries (f newVal oldVal).
+-- the provided function @f@ to merge duplicate entries with
+-- @(f newVal oldVal)@.
 --
--- For example:
+-- === Examples
 --
--- > fromListWith (+) [ (x, 1) | x <- xs ]
+-- Given a list @xs@, create a map with the number of occurrences of each
+-- element in @xs@:
 --
--- will create a map with number of occurrences of each element in xs.
+-- > let xs = ['a', 'b', 'a']
+-- > in fromListWith (+) [ (x, 1) | x <- xs ]
+-- >
+-- > = fromList [('a', 2), ('b', 1)]
 --
--- > fromListWith (++) [ (k, [v]) | (k, v) <- xs ]
+-- Given a list of key-value pairs @xs :: [(k, v)]@, group all values by their
+-- keys and return a @HashMap k [v]@.
 --
--- will group all values by their keys in a list 'xs :: [(k, v)]' and
--- return a 'HashMap k [v]'.
+-- > let xs = ('a', 1), ('b', 2), ('a', 3)]
+-- > in fromListWith (++) [ (k, [v]) | (k, v) <- xs ]
+-- >
+-- > = fromList [('a', [3, 1]), ('b', [2])]
+--
+-- Note that the lists in the resulting map contain elements in reverse order
+-- from their occurences in the original list.
+--
+-- More generally, duplicate entries are accumulated as follows;
+-- this matters when @f@ is not commutative or not associative.
+--
+-- > fromListWith f [(k, a), (k, b), (k, c), (k, d)]
+-- > = fromList [(k, f d (f c (f b a)))]
 fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v
 fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty
 {-# INLINE fromListWith #-}
+
+-- | /O(n*log n)/ Construct a map from a list of elements.  Uses
+-- the provided function to merge duplicate entries.
+--
+-- === Examples
+--
+-- Given a list of key-value pairs where the keys are of different flavours, e.g:
+--
+-- > data Key = Div | Sub
+--
+-- and the values need to be combined differently when there are duplicates,
+-- depending on the key:
+--
+-- > combine Div = div
+-- > combine Sub = (-)
+--
+-- then @fromListWithKey@ can be used as follows:
+--
+-- > fromListWithKey combine [(Div, 2), (Div, 6), (Sub, 2), (Sub, 3)]
+-- > = fromList [(Div, 3), (Sub, 1)]
+--
+-- More generally, duplicate entries are accumulated as follows;
+--
+-- > fromListWith f [(k, a), (k, b), (k, c), (k, d)]
+-- > = fromList [(k, f k d (f k c (f k b a)))]
+--
+-- @since 0.2.11
+fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
+fromListWithKey f = L.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty
+{-# INLINE fromListWithKey #-}
 
 ------------------------------------------------------------------------
 -- Array operations
diff --git a/Data/HashSet/Base.hs b/Data/HashSet/Base.hs
--- a/Data/HashSet/Base.hs
+++ b/Data/HashSet/Base.hs
@@ -55,8 +55,10 @@
     , intersection
 
     -- * Folds
-    , foldl'
     , foldr
+    , foldr'
+    , foldl
+    , foldl'
 
     -- * Filter
     , filter
@@ -77,7 +79,9 @@
 
 import Control.DeepSeq (NFData(..))
 import Data.Data hiding (Typeable)
-import Data.HashMap.Base (HashMap, foldrWithKey, equalKeys, equalKeys1)
+import Data.HashMap.Base
+  ( HashMap, foldMapWithKey, foldlWithKey, foldrWithKey
+  , equalKeys, equalKeys1)
 import Data.Hashable (Hashable(hashWithSalt))
 #if __GLASGOW_HASKELL__ >= 711
 import Data.Semigroup (Semigroup(..))
@@ -85,7 +89,7 @@
 import Data.Monoid (Monoid(..))
 #endif
 import GHC.Exts (build)
-import Prelude hiding (filter, foldr, map, null)
+import Prelude hiding (filter, foldr, foldl, map, null)
 import qualified Data.Foldable as Foldable
 import qualified Data.HashMap.Base as H
 import qualified Data.List as List
@@ -119,6 +123,24 @@
     rnf = rnf . asMap
     {-# INLINE rnf #-}
 
+-- | Note that, in the presence of hash collisions, equal @HashSet@s may
+-- behave differently, i.e. substitutivity may be violated:
+--
+-- >>> data D = A | B deriving (Eq, Show)
+-- >>> instance Hashable D where hashWithSalt salt _d = salt
+--
+-- >>> x = fromList [A, B]
+-- >>> y = fromList [B, A]
+--
+-- >>> x == y
+-- True
+-- >>> toList x
+-- [A,B]
+-- >>> toList y
+-- [B,A]
+--
+-- In general, the lack of substitutivity can be observed with any function
+-- that depends on the key ordering, such as folds and traversals.
 instance (Eq a) => Eq (HashSet a) where
     HashSet a == HashSet b = equalKeys a b
     {-# INLINE (==) #-}
@@ -138,15 +160,54 @@
 #endif
 
 instance Foldable.Foldable HashSet where
-    foldr = Data.HashSet.Base.foldr
+    foldMap f = foldMapWithKey (\a _ -> f a) . asMap
+    foldr = foldr
     {-# INLINE foldr #-}
+    foldl = foldl
+    {-# INLINE foldl #-}
+    foldl' = foldl'
+    {-# INLINE foldl' #-}
+    foldr' = foldr'
+    {-# INLINE foldr' #-}
+#if MIN_VERSION_base(4,8,0)
+    toList = toList
+    {-# INLINE toList #-}
+    null = null
+    {-# INLINE null #-}
+    length = size
+    {-# INLINE length #-}
+#endif
 
 #if __GLASGOW_HASKELL__ >= 711
+-- | '<>' = 'union'
+--
+-- /O(n+m)/
+--
+-- To obtain good performance, the smaller set must be presented as
+-- the first argument.
+--
+-- ==== __Examples__
+--
+-- >>> fromList [1,2] <> fromList [2,3]
+-- fromList [1,2,3]
 instance (Hashable a, Eq a) => Semigroup (HashSet a) where
     (<>) = union
     {-# INLINE (<>) #-}
 #endif
 
+-- | 'mempty' = 'empty'
+--
+-- 'mappend' = 'union'
+--
+-- /O(n+m)/
+--
+-- To obtain good performance, the smaller set must be presented as
+-- the first argument.
+--
+-- ==== __Examples__
+--
+-- >>> mappend (fromList [1,2]) (fromList [2,3])
+-- fromList [1,2,3]
 instance (Hashable a, Eq a) => Monoid (HashSet a) where
     mempty = empty
     {-# INLINE mempty #-}
@@ -225,6 +286,11 @@
 --
 -- To obtain good performance, the smaller set must be presented as
 -- the first argument.
+--
+-- ==== __Examples__
+--
+-- >>> union (fromList [1,2]) (fromList [2,3])
+-- fromList [1,2,3]
 union :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
 union s1 s2 = HashSet $ H.union (asMap s1) (asMap s2)
 {-# INLINE union #-}
@@ -295,11 +361,29 @@
 
 -- | /O(n)/ Reduce this set by applying a binary operator to all
 -- elements, using the given starting value (typically the
+-- right-identity of the operator). Each application of the operator
+-- is evaluated before before using the result in the next
+-- application. This function is strict in the starting value.
+foldr' :: (b -> a -> a) -> a -> HashSet b -> a
+foldr' f z0 = H.foldrWithKey' g z0 . asMap
+  where g k _ z = f k z
+{-# INLINE foldr' #-}
+
+-- | /O(n)/ Reduce this set by applying a binary operator to all
+-- elements, using the given starting value (typically the
 -- right-identity of the operator).
 foldr :: (b -> a -> a) -> a -> HashSet b -> a
 foldr f z0 = foldrWithKey g z0 . asMap
   where g k _ z = f k z
 {-# INLINE foldr #-}
+
+-- | /O(n)/ Reduce this set by applying a binary operator to all
+-- elements, using the given starting value (typically the
+-- left-identity of the operator).
+foldl :: (a -> b -> a) -> a -> HashSet b -> a
+foldl f z0 = foldlWithKey g z0 . asMap
+  where g z k _ = f z k
+{-# INLINE foldl #-}
 
 -- | /O(n)/ Filter this set by retaining only elements satisfying a
 -- predicate.
diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs
--- a/benchmarks/Benchmarks.hs
+++ b/benchmarks/Benchmarks.hs
@@ -4,10 +4,10 @@
 
 import Control.DeepSeq
 import Control.DeepSeq.Generics (genericRnf)
-import Criterion.Main (bench, bgroup, defaultMain, env, nf, whnf)
+import Gauge (bench, bgroup, defaultMain, env, nf, whnf)
 import Data.Bits ((.&.))
 import Data.Functor.Identity
-import Data.Hashable (Hashable)
+import Data.Hashable (Hashable, hash)
 import qualified Data.ByteString as BS
 import qualified "hashmap" Data.HashMap as IHM
 import qualified Data.HashMap.Strict as HM
@@ -179,6 +179,10 @@
           , bgroup "fromList"
             [ bench "String" $ whnf IHM.fromList elems
             , bench "ByteString" $ whnf IHM.fromList elemsBS
+            ]
+          , bgroup "hash"
+            [ bench "String" $ whnf hash hm
+            , bench "ByteString" $ whnf hash hmbs
             ]
           ]
 
diff --git a/tests/HashMapProperties.hs b/tests/HashMapProperties.hs
--- a/tests/HashMapProperties.hs
+++ b/tests/HashMapProperties.hs
@@ -7,6 +7,9 @@
 
 import Control.Monad ( guard )
 import qualified Data.Foldable as Foldable
+#if MIN_VERSION_base(4,10,0)
+import Data.Bifoldable
+#endif
 import Data.Function (on)
 import Data.Hashable (Hashable(hashWithSalt))
 import qualified Data.List as L
@@ -122,6 +125,9 @@
 pLookup :: Key -> [(Key, Int)] -> Bool
 pLookup k = M.lookup k `eq` HM.lookup k
 
+pLookupOperator :: Key -> [(Key, Int)] -> Bool
+pLookupOperator k = M.lookup k `eq` (HM.!? k)
+
 pInsert :: Key -> Int -> [(Key, Int)] -> Bool
 pInsert k v = M.insert k v `eq_` HM.insert k v
 
@@ -285,22 +291,57 @@
 pFoldr :: [(Int, Int)] -> Bool
 pFoldr = (L.sort . M.foldr (:) []) `eq` (L.sort . HM.foldr (:) [])
 
+pFoldl :: [(Int, Int)] -> Bool
+pFoldl = (L.sort . M.foldl (flip (:)) []) `eq` (L.sort . HM.foldl (flip (:)) [])
+
+#if MIN_VERSION_base(4,10,0)
+pBifoldMap :: [(Int, Int)] -> Bool
+pBifoldMap xs = concatMap f (HM.toList m) == bifoldMap (:[]) (:[]) m
+  where f (k, v) = [k, v]
+        m = HM.fromList xs
+
+pBifoldr :: [(Int, Int)] -> Bool
+pBifoldr xs = concatMap f (HM.toList m) == bifoldr (:) (:) [] m
+  where f (k, v) = [k, v]
+        m = HM.fromList xs
+
+pBifoldl :: [(Int, Int)] -> Bool
+pBifoldl xs = reverse (concatMap f $ HM.toList m) == bifoldl (flip (:)) (flip (:)) [] m
+  where f (k, v) = [k, v]
+        m = HM.fromList xs
+#endif
+
 pFoldrWithKey :: [(Int, Int)] -> Bool
 pFoldrWithKey = (sortByKey . M.foldrWithKey f []) `eq`
                 (sortByKey . HM.foldrWithKey f [])
   where f k v z = (k, v) : z
 
-pFoldl' :: Int -> [(Int, Int)] -> Bool
-pFoldl' z0 = foldlWithKey'Map (\ z _ v -> v + z) z0 `eq` HM.foldl' (+) z0
+pFoldMapWithKey :: [(Int, Int)] -> Bool
+pFoldMapWithKey = (sortByKey . M.foldMapWithKey f) `eq`
+                  (sortByKey . HM.foldMapWithKey f)
+  where f k v = [(k, v)]
 
-foldlWithKey'Map :: (b -> k -> a -> b) -> b -> M.Map k a -> b
-#if MIN_VERSION_containers(4,2,0)
-foldlWithKey'Map = M.foldlWithKey'
-#else
--- Equivalent except for bottoms, which we don't test.
-foldlWithKey'Map = M.foldlWithKey
-#endif
+pFoldrWithKey' :: [(Int, Int)] -> Bool
+pFoldrWithKey' = (sortByKey . M.foldrWithKey' f []) `eq`
+                 (sortByKey . HM.foldrWithKey' f [])
+  where f k v z = (k, v) : z
 
+pFoldlWithKey :: [(Int, Int)] -> Bool
+pFoldlWithKey = (sortByKey . M.foldlWithKey f []) `eq`
+                (sortByKey . HM.foldlWithKey f [])
+  where f z k v = (k, v) : z
+
+pFoldlWithKey' :: [(Int, Int)] -> Bool
+pFoldlWithKey' = (sortByKey . M.foldlWithKey' f []) `eq`
+                 (sortByKey . HM.foldlWithKey' f [])
+  where f z k v = (k, v) : z
+
+pFoldl' :: [(Int, Int)] -> Bool
+pFoldl' = (L.sort . M.foldl' (flip (:)) []) `eq` (L.sort . HM.foldl' (flip (:)) [])
+
+pFoldr' :: [(Int, Int)] -> Bool
+pFoldr' = (L.sort . M.foldr' (:) []) `eq` (L.sort . HM.foldr' (:) [])
+
 ------------------------------------------------------------------------
 -- ** Filter
 
@@ -322,14 +363,32 @@
 ------------------------------------------------------------------------
 -- ** Conversions
 
+-- The free magma is used to test that operations are applied in the
+-- same order.
+data Magma a
+  = Leaf a
+  | Op (Magma a) (Magma a)
+  deriving (Show, Eq, Ord)
+
+instance Hashable a => Hashable (Magma a) where
+  hashWithSalt s (Leaf a) = hashWithSalt s (hashWithSalt (1::Int) a)
+  hashWithSalt s (Op m n) = hashWithSalt s (hashWithSalt (hashWithSalt (2::Int) m) n)
+
 -- 'eq_' already calls fromList.
 pFromList :: [(Key, Int)] -> Bool
 pFromList = id `eq_` id
 
 pFromListWith :: [(Key, Int)] -> Bool
-pFromListWith kvs = (M.toAscList $ M.fromListWith (+) kvs) ==
-                    (toAscList $ HM.fromListWith (+) kvs)
+pFromListWith kvs = (M.toAscList $ M.fromListWith Op kvsM) ==
+                    (toAscList $ HM.fromListWith Op kvsM)
+  where kvsM = fmap (fmap Leaf) kvs
 
+pFromListWithKey :: [(Key, Int)] -> Bool
+pFromListWithKey kvs = (M.toAscList $ M.fromListWithKey combine kvsM) ==
+                       (toAscList $ HM.fromListWithKey combine kvsM)
+  where kvsM = fmap (\(K k,v) -> (Leaf k, Leaf v)) kvs
+        combine k v1 v2 = Op k (Op v1 v2)
+
 pToList :: [(Key, Int)] -> Bool
 pToList = M.toAscList `eq` toAscList
 
@@ -363,6 +422,7 @@
       [ testProperty "size" pSize
       , testProperty "member" pMember
       , testProperty "lookup" pLookup
+      , testProperty "!?" pLookupOperator
       , testProperty "insert" pInsert
       , testProperty "delete" pDelete
       , testProperty "deleteCollision" pDeleteCollision
@@ -391,8 +451,19 @@
     -- Folds
     , testGroup "folds"
       [ testProperty "foldr" pFoldr
+      , testProperty "foldl" pFoldl
+#if MIN_VERSION_base(4,10,0)
+      , testProperty "bifoldMap" pBifoldMap
+      , testProperty "bifoldr" pBifoldr
+      , testProperty "bifoldl" pBifoldl
+#endif
       , testProperty "foldrWithKey" pFoldrWithKey
+      , testProperty "foldlWithKey" pFoldlWithKey
+      , testProperty "foldrWithKey'" pFoldrWithKey'
+      , testProperty "foldlWithKey'" pFoldlWithKey'
       , testProperty "foldl'" pFoldl'
+      , testProperty "foldr'" pFoldr'
+      , testProperty "foldMapWithKey" pFoldMapWithKey
       ]
     , testGroup "difference and intersection"
       [ testProperty "difference" pDifference
@@ -414,6 +485,7 @@
       , testProperty "keys" pKeys
       , testProperty "fromList" pFromList
       , testProperty "fromListWith" pFromListWith
+      , testProperty "fromListWithKey" pFromListWithKey
       , testProperty "toList" pToList
       ]
     ]
diff --git a/tests/Regressions.hs b/tests/Regressions.hs
--- a/tests/Regressions.hs
+++ b/tests/Regressions.hs
@@ -1,10 +1,21 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
 module Main where
 
 import Control.Applicative ((<$>))
+import Control.Exception (evaluate)
 import Control.Monad (replicateM)
+import Data.Hashable (Hashable(..))
 import qualified Data.HashMap.Strict as HM
+import qualified Data.HashMap.Lazy as HML
 import Data.List (delete)
 import Data.Maybe
+import GHC.Exts (touch#)
+import GHC.IO (IO (..))
+import System.Mem (performGC)
+import System.Mem.Weak (mkWeakPtr, deRefWeak)
+import System.Random (randomIO)
 import Test.HUnit (Assertion, assert)
 import Test.Framework (Test, defaultMain)
 import Test.Framework.Providers.HUnit (testCase)
@@ -72,6 +83,48 @@
 mapFromKeys keys = HM.fromList (zip keys (repeat ()))
 
 ------------------------------------------------------------------------
+-- Issue #254
+
+-- Key type that always collides.
+newtype KC = KC Int
+  deriving (Eq, Ord, Show)
+instance Hashable KC where
+  hashWithSalt salt _ = salt
+
+touch :: a -> IO ()
+touch a = IO (\s -> (# touch# a s, () #))
+
+-- We want to make sure that old values in the HashMap are evicted when new values are inserted,
+-- even if they aren't evaluated. To do that, we use the WeakPtr trick described at
+-- http://simonmar.github.io/posts/2018-06-20-Finding-fixing-space-leaks.html.
+-- We insert a value named oldV into the HashMap, then insert over it, checking oldV is no longer reachable.
+--
+-- To make the test robust, it's important that oldV isn't hoisted up to the top or shared.
+-- To do that, we generate it randomly.
+issue254Lazy :: Assertion
+issue254Lazy = do
+  i :: Int <- randomIO
+  let oldV = error $ "Should not be evaluated: " ++ show i
+  weakV <- mkWeakPtr oldV Nothing -- add the ability to test whether oldV is alive
+  mp <- evaluate $ HML.insert (KC 1) (error "Should not be evaluated") $ HML.fromList [(KC 0, "1"), (KC 1, oldV)]
+  performGC
+  res <- deRefWeak weakV -- gives Just if oldV is still alive
+  touch mp -- makes sure that we didn't GC away the whole HashMap, just oldV
+  assert $ isNothing res
+
+-- Like issue254Lazy, but using strict HashMap
+issue254Strict :: Assertion
+issue254Strict = do
+  i :: Int <- randomIO
+  let oldV = show i
+  weakV <- mkWeakPtr oldV Nothing
+  mp <- evaluate $ HM.insert (KC 1) "3" $ HM.fromList [(KC 0, "1"), (KC 1, oldV)]
+  performGC
+  res <- deRefWeak weakV
+  touch mp
+  assert $ isNothing res
+
+------------------------------------------------------------------------
 -- * Test list
 
 tests :: [Test]
@@ -80,6 +133,8 @@
       testCase "issue32" issue32
     , testCase "issue39a" issue39
     , testProperty "issue39b" propEqAfterDelete
+    , testCase "issue254 lazy" issue254Lazy
+    , testCase "issue254 strict" issue254Strict
     ]
 
 ------------------------------------------------------------------------
diff --git a/tests/Strictness.hs b/tests/Strictness.hs
--- a/tests/Strictness.hs
+++ b/tests/Strictness.hs
@@ -55,6 +55,9 @@
 pLookupDefaultKeyStrict :: Int -> HashMap Key Int -> Bool
 pLookupDefaultKeyStrict def m = isBottom $ HM.lookupDefault def bottom m
 
+pFindWithDefaultKeyStrict :: Int -> HashMap Key Int -> Bool
+pFindWithDefaultKeyStrict def m = isBottom $ HM.findWithDefault def bottom m
+
 pAdjustKeyStrict :: (Int -> Int) -> HashMap Key Int -> Bool
 pAdjustKeyStrict f m = isBottom $ HM.adjust f bottom m
 
@@ -161,6 +164,7 @@
       , testProperty "member is key-strict" $ keyStrict HM.member
       , testProperty "lookup is key-strict" $ keyStrict HM.lookup
       , testProperty "lookupDefault is key-strict" pLookupDefaultKeyStrict
+      , testProperty "findWithDefault is key-strict" pFindWithDefaultKeyStrict
       , testProperty "! is key-strict" $ keyStrict (flip (HM.!))
       , testProperty "delete is key-strict" $ keyStrict HM.delete
       , testProperty "adjust is key-strict" pAdjustKeyStrict
diff --git a/unordered-containers.cabal b/unordered-containers.cabal
--- a/unordered-containers.cabal
+++ b/unordered-containers.cabal
@@ -1,5 +1,5 @@
 name:           unordered-containers
-version:        0.2.10.0
+version:        0.2.11.0
 synopsis:       Efficient hashing-based container types
 description:
   Efficient hashing-based container types.  The containers have been
@@ -11,17 +11,26 @@
 license:        BSD3
 license-file:   LICENSE
 author:         Johan Tibell
-maintainer:     johan.tibell@gmail.com
-Homepage:       https://github.com/tibbe/unordered-containers
-bug-reports:    https://github.com/tibbe/unordered-containers/issues
+maintainer:     johan.tibell@gmail.com, David.Feuer@gmail.com
+Homepage:       https://github.com/haskell-unordered-containers/unordered-containers
+bug-reports:    https://github.com/haskell-unordered-containers/unordered-containers/issues
 copyright:      2010-2014 Johan Tibell
                 2010 Edward Z. Yang
 category:       Data
 build-type:     Simple
 cabal-version:  >=1.10
 extra-source-files: CHANGES.md
-tested-with:    GHC==8.4.1, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4
 
+tested-with:
+  GHC ==8.10.1
+   || ==8.8.3
+   || ==8.6.5
+   || ==8.4.4
+   || ==8.2.2
+   || ==8.0.2
+   || ==7.10.3
+   || ==7.8.4
+
 flag debug
   description:  Enable debug support
   default:      False
@@ -43,7 +52,7 @@
   build-depends:
     base >= 4.7 && < 5,
     deepseq >= 1.1,
-    hashable >= 1.0.1.1 && < 1.3
+    hashable >= 1.0.1.1 && < 1.4
 
   default-language: Haskell2010
 
@@ -146,6 +155,7 @@
     hashable >= 1.0.1.1,
     HUnit,
     QuickCheck >= 2.4.0.1,
+    random,
     test-framework >= 0.3.3,
     test-framework-hunit,
     test-framework-quickcheck2,
@@ -175,44 +185,31 @@
   cpp-options: -DASSERTS
 
 benchmark benchmarks
-  -- We cannot depend on the unordered-containers library directly as
-  -- that creates a dependency cycle.
-  hs-source-dirs: . benchmarks
-
+  hs-source-dirs: benchmarks
   main-is: Benchmarks.hs
   type: exitcode-stdio-1.0
 
   other-modules:
-    Data.HashMap.Array
-    Data.HashMap.Base
-    Data.HashMap.Lazy
-    Data.HashMap.Strict
-    Data.HashMap.Strict.Base
-    Data.HashMap.Unsafe
-    Data.HashMap.UnsafeShift
-    Data.HashSet
-    Data.HashSet.Base
     Util.ByteString
-    Util.Int
     Util.String
+    Util.Int
 
   build-depends:
     base >= 4.8.0,
     bytestring,
     containers,
-    criterion >= 1.0 && < 1.3,
+    gauge >= 0.2.5 && < 0.3,
     deepseq >= 1.1,
     deepseq-generics,
     hashable >= 1.0.1.1,
     hashmap,
     mtl,
-    random
+    random,
+    unordered-containers
 
   default-language: Haskell2010
   ghc-options: -Wall -O2 -rtsopts -fwarn-tabs -ferror-spans
-  if flag(debug)
-    cpp-options: -DASSERTS
 
 source-repository head
   type:     git
-  location: https://github.com/tibbe/unordered-containers.git
+  location: https://github.com/haskell-unordered-containers/unordered-containers.git
