Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added `foldMapWithKey`. #24

Merged
merged 1 commit into from

2 participants

Edward Kmett Milan Straka
Edward Kmett

I've needed this repeatedly over the years and it makes an asymptotic difference in parts of ekmett/lens, so I figured I'd submit it.

This is asymptotically more efficient than using fold . mapWithKey for many monoids because it doesn't have to build up an entire new map just to fold it.

There isn't a sensible strict variant.

Milan Straka
Collaborator

Hi,

thanks :)

I assume you mean Foldable.fold instead of Prelude.fold in the @'foldMapWithKey' f = 'Prelude.fold' . 'mapWithKey' f@ documentation comment.

I am not sure why is foldMapWithKey f = foldlWithKey (\a k b -> a `mappend` f k b) mempty asymptotically less efficient than your definition. I realize that my and your definition differ in bracket positions (the foldlWithKey creates a linear "string" of mappends, while your definition is recursive), which could make a difference when mappend ignores an argument -- is this the key difference? If so, the properties of your definition should be mentioned in the documentation so that users can rely on this behaviour.

Nevertheless, could we move this discussion to the libraries@haskell... mailing list? Please create a proposal there, so others can be part of the discussion too.

Cheers,
Milan

Edward Kmett

I originally just sent it here because I didn't think there was a lot to bikeshed on the issue, but the libraries@ ticket has been created.

Edward Kmett

Consider the Monoid (First a, Last a) -- one of those is going to have to do a lot of work no matter which direction you choose between foldr and foldl. I can extract this with foldMap, but if I want the keys I have to start over, or I have to implement foldMapWithKey using traverseWithKey on Const.

Milan Straka foxik merged commit 0eb7a34 into from
Milan Straka
Collaborator

Sorry it took such a long time, I forgot about the issue as there was no summary on libraries@...

Cheers,
Milan

Edward Kmett

Thanks! Sorry for not following up sooner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 25, 2012
  1. Edward Kmett

    Added `foldMapWithKey`.

    ekmett authored
This page is out of date. Refresh to see the latest.
15 Data/IntMap/Base.hs
View
@@ -123,6 +123,8 @@ module Data.IntMap.Base (
, foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
@@ -1670,6 +1672,19 @@ foldlWithKey' f z = \t -> -- Use lambda t to be inlinable with two argument
go z' (Bin _ _ l r) = go (go z' l) r
{-# INLINE foldlWithKey' #-}
+-- | /O(n)/. Fold the keys and values in the map using the given monoid, such that
+--
+-- @'foldMapWithKey' f = 'Prelude.fold' . 'mapWithKey' f@
+--
+-- This can be an asymptotically faster than 'foldrWithKey' or 'foldlWithKey' for some monoids.
+foldMapWithKey :: Monoid m => (Key -> a -> m) -> IntMap a -> m
+foldMapWithKey f = go
+ where
+ go Nil = mempty
+ go (Tip kx x) = f kx x
+ go (Bin _ _ l r) = go l `mappend` go r
+{-# INLINE foldMapWithKey #-}
+
{--------------------------------------------------------------------
List variations
--------------------------------------------------------------------}
2  Data/IntMap/Lazy.hs
View
@@ -133,6 +133,8 @@ module Data.IntMap.Lazy (
, IM.foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
2  Data/IntMap/Strict.hs
View
@@ -139,6 +139,8 @@ module Data.IntMap.Strict (
, foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
14 Data/Map/Base.hs
View
@@ -169,6 +169,8 @@ module Data.Map.Base (
, foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
@@ -1873,6 +1875,18 @@ foldlWithKey' f z = go z
go z' (Bin _ kx x l r) = go (f (go z' l) kx x) r
{-# INLINE foldlWithKey' #-}
+-- | /O(n)/. Fold the keys and values in the map using the given monoid, such that
+--
+-- @'foldMapWithKey' f = 'Prelude.fold' . 'mapWithKey' f@
+--
+-- This can be an asymptotically faster than 'foldrWithKey' or 'foldlWithKey' for some monoids.
+foldMapWithKey :: Monoid m => (k -> a -> m) -> Map k a -> m
+foldMapWithKey f = go
+ where
+ go Tip = mempty
+ go (Bin _ k v l r) = go l `mappend` f k v `mappend` go r
+{-# INLINE foldMapWithKey #-}
+
{--------------------------------------------------------------------
List variations
--------------------------------------------------------------------}
2  Data/Map/Lazy.hs
View
@@ -129,6 +129,8 @@ module Data.Map.Lazy (
, M.foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
2  Data/Map/Strict.hs
View
@@ -136,6 +136,8 @@ module Data.Map.Strict
, foldl
, foldrWithKey
, foldlWithKey
+ , foldMapWithKey
+
-- ** Strict folds
, foldr'
, foldl'
Something went wrong with that request. Please try again.