Skip to content

Commit c16f7a4

Browse files
authored
Merge pull request #224 from treeowl/more-foldable
Add more Foldable methods
2 parents 06834a3 + 1a33149 commit c16f7a4

File tree

8 files changed

+203
-32
lines changed

8 files changed

+203
-32
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## next
22

33
* Add `HashMap.findWithDefault` (deprecates `HashMap.lookupDefault`)
4+
5+
* Add more folding functions and use them in `Foldable` instances.
46

57
* Add `HashMap.!?`, a flipped version of `lookup`.
68

Data/HashMap/Array.hs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ module Data.HashMap.Array
4141
, copyM
4242

4343
-- * Folds
44+
, foldl
4445
, foldl'
4546
, foldr
47+
, foldr'
48+
, foldMap
4649

4750
, thaw
4851
, map
@@ -63,9 +66,9 @@ import GHC.ST (ST(..))
6366
import Control.Monad.ST (stToIO)
6467

6568
#if __GLASGOW_HASKELL__ >= 709
66-
import Prelude hiding (filter, foldr, length, map, read, traverse)
69+
import Prelude hiding (filter, foldMap, foldr, foldl, length, map, read, traverse)
6770
#else
68-
import Prelude hiding (filter, foldr, length, map, read)
71+
import Prelude hiding (filter, foldr, foldl, length, map, read)
6972
#endif
7073

7174
#if __GLASGOW_HASKELL__ >= 710
@@ -79,6 +82,7 @@ import GHC.Exts (Array#, newArray#, readArray#, writeArray#,
7982
indexArray#, unsafeFreezeArray#, unsafeThawArray#,
8083
MutableArray#, sizeofArray#, copyArray#, thawArray#,
8184
sizeofMutableArray#, copyMutableArray#, cloneMutableArray#)
85+
import Data.Monoid (Monoid (..))
8286
#endif
8387

8488
#if defined(ASSERTS)
@@ -418,6 +422,15 @@ foldl' f = \ z0 ary0 -> go ary0 (length ary0) 0 z0
418422
(# x #) -> go ary n (i+1) (f z x)
419423
{-# INLINE foldl' #-}
420424

425+
foldr' :: (a -> b -> b) -> b -> Array a -> b
426+
foldr' f = \ z0 ary0 -> go ary0 (length ary0 - 1) z0
427+
where
428+
go !_ary (-1) z = z
429+
go !ary i !z
430+
| (# x #) <- index# ary i
431+
= go ary (i - 1) (f x z)
432+
{-# INLINE foldr' #-}
433+
421434
foldr :: (a -> b -> b) -> b -> Array a -> b
422435
foldr f = \ z0 ary0 -> go ary0 (length ary0) 0 z0
423436
where
@@ -428,6 +441,29 @@ foldr f = \ z0 ary0 -> go ary0 (length ary0) 0 z0
428441
(# x #) -> f x (go ary n (i+1) z)
429442
{-# INLINE foldr #-}
430443

444+
foldl :: (b -> a -> b) -> b -> Array a -> b
445+
foldl f = \ z0 ary0 -> go ary0 (length ary0 - 1) z0
446+
where
447+
go _ary (-1) z = z
448+
go ary i z
449+
| (# x #) <- index# ary i
450+
= f (go ary (i - 1) z) x
451+
{-# INLINE foldl #-}
452+
453+
-- We go to a bit of trouble here to avoid appending an extra mempty.
454+
-- The below implementation is by Mateusz Kowalczyk, who indicates that
455+
-- benchmarks show it to be faster than one that avoids lifting out
456+
-- lst.
457+
foldMap :: Monoid m => (a -> m) -> Array a -> m
458+
foldMap f = \ary0 -> case length ary0 of
459+
0 -> mempty
460+
len ->
461+
let !lst = len - 1
462+
go i | (# x #) <- index# ary0 i, let fx = f x =
463+
if i == lst then fx else fx `mappend` go (i + 1)
464+
in go 0
465+
{-# INLINE foldMap #-}
466+
431467
undefinedElem :: a
432468
undefinedElem = error "Data.HashMap.Array: Undefined element"
433469
{-# NOINLINE undefinedElem #-}

Data/HashMap/Base.hs

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,15 @@ module Data.HashMap.Base
5858
, intersectionWithKey
5959

6060
-- * Folds
61+
, foldr'
6162
, foldl'
63+
, foldrWithKey'
6264
, foldlWithKey'
6365
, foldr
66+
, foldl
6467
, foldrWithKey
68+
, foldlWithKey
69+
, foldMapWithKey
6570

6671
-- * Filter
6772
, mapMaybe
@@ -128,7 +133,7 @@ import Data.Data hiding (Typeable)
128133
import qualified Data.Foldable as Foldable
129134
import qualified Data.List as L
130135
import GHC.Exts ((==#), build, reallyUnsafePtrEquality#)
131-
import Prelude hiding (filter, foldr, lookup, map, null, pred)
136+
import Prelude hiding (filter, foldl, foldr, lookup, map, null, pred)
132137
import Text.Read hiding (step)
133138

134139
import qualified Data.HashMap.Array as A
@@ -200,14 +205,20 @@ instance Functor (HashMap k) where
200205
fmap = map
201206

202207
instance Foldable.Foldable (HashMap k) where
203-
foldr = Data.HashMap.Base.foldr
208+
foldMap f = foldMapWithKey (\ _k v -> f v)
209+
{-# INLINE foldMap #-}
210+
foldr = foldr
204211
{-# INLINE foldr #-}
205-
foldl' = Data.HashMap.Base.foldl'
212+
foldl = foldl
213+
{-# INLINE foldl #-}
214+
foldr' = foldr'
215+
{-# INLINE foldr' #-}
216+
foldl' = foldl'
206217
{-# INLINE foldl' #-}
207218
#if MIN_VERSION_base(4,8,0)
208-
null = Data.HashMap.Base.null
219+
null = null
209220
{-# INLINE null #-}
210-
length = Data.HashMap.Base.size
221+
length = size
211222
{-# INLINE length #-}
212223
#endif
213224

@@ -1584,6 +1595,15 @@ foldl' :: (a -> v -> a) -> a -> HashMap k v -> a
15841595
foldl' f = foldlWithKey' (\ z _ v -> f z v)
15851596
{-# INLINE foldl' #-}
15861597

1598+
-- | /O(n)/ Reduce this map by applying a binary operator to all
1599+
-- elements, using the given starting value (typically the
1600+
-- right-identity of the operator). Each application of the operator
1601+
-- is evaluated before using the result in the next application.
1602+
-- This function is strict in the starting value.
1603+
foldr' :: (v -> a -> a) -> a -> HashMap k v -> a
1604+
foldr' f = foldrWithKey' (\ _ v z -> f v z)
1605+
{-# INLINE foldr' #-}
1606+
15871607
-- | /O(n)/ Reduce this map by applying a binary operator to all
15881608
-- elements, using the given starting value (typically the
15891609
-- left-identity of the operator). Each application of the operator
@@ -1599,26 +1619,73 @@ foldlWithKey' f = go
15991619
go z (Collision _ ary) = A.foldl' (\ z' (L k v) -> f z' k v) z ary
16001620
{-# INLINE foldlWithKey' #-}
16011621

1622+
-- | /O(n)/ Reduce this map by applying a binary operator to all
1623+
-- elements, using the given starting value (typically the
1624+
-- right-identity of the operator). Each application of the operator
1625+
-- is evaluated before using the result in the next application.
1626+
-- This function is strict in the starting value.
1627+
foldrWithKey' :: (k -> v -> a -> a) -> a -> HashMap k v -> a
1628+
foldrWithKey' f = flip go
1629+
where
1630+
go Empty z = z
1631+
go (Leaf _ (L k v)) !z = f k v z
1632+
go (BitmapIndexed _ ary) !z = A.foldr' go z ary
1633+
go (Full ary) !z = A.foldr' go z ary
1634+
go (Collision _ ary) !z = A.foldr' (\ (L k v) z' -> f k v z') z ary
1635+
{-# INLINE foldrWithKey' #-}
1636+
16021637
-- | /O(n)/ Reduce this map by applying a binary operator to all
16031638
-- elements, using the given starting value (typically the
16041639
-- right-identity of the operator).
16051640
foldr :: (v -> a -> a) -> a -> HashMap k v -> a
16061641
foldr f = foldrWithKey (const f)
16071642
{-# INLINE foldr #-}
16081643

1644+
-- | /O(n)/ Reduce this map by applying a binary operator to all
1645+
-- elements, using the given starting value (typically the
1646+
-- left-identity of the operator).
1647+
foldl :: (a -> v -> a) -> a -> HashMap k v -> a
1648+
foldl f = foldlWithKey (\a _k v -> f a v)
1649+
{-# INLINE foldl #-}
1650+
16091651
-- | /O(n)/ Reduce this map by applying a binary operator to all
16101652
-- elements, using the given starting value (typically the
16111653
-- right-identity of the operator).
16121654
foldrWithKey :: (k -> v -> a -> a) -> a -> HashMap k v -> a
1613-
foldrWithKey f = go
1655+
foldrWithKey f = flip go
16141656
where
1615-
go z Empty = z
1616-
go z (Leaf _ (L k v)) = f k v z
1617-
go z (BitmapIndexed _ ary) = A.foldr (flip go) z ary
1618-
go z (Full ary) = A.foldr (flip go) z ary
1619-
go z (Collision _ ary) = A.foldr (\ (L k v) z' -> f k v z') z ary
1657+
go Empty z = z
1658+
go (Leaf _ (L k v)) z = f k v z
1659+
go (BitmapIndexed _ ary) z = A.foldr go z ary
1660+
go (Full ary) z = A.foldr go z ary
1661+
go (Collision _ ary) z = A.foldr (\ (L k v) z' -> f k v z') z ary
16201662
{-# INLINE foldrWithKey #-}
16211663

1664+
-- | /O(n)/ Reduce this map by applying a binary operator to all
1665+
-- elements, using the given starting value (typically the
1666+
-- left-identity of the operator).
1667+
foldlWithKey :: (a -> k -> v -> a) -> a -> HashMap k v -> a
1668+
foldlWithKey f = go
1669+
where
1670+
go z Empty = z
1671+
go z (Leaf _ (L k v)) = f z k v
1672+
go z (BitmapIndexed _ ary) = A.foldl go z ary
1673+
go z (Full ary) = A.foldl go z ary
1674+
go z (Collision _ ary) = A.foldl (\ z' (L k v) -> f z' k v) z ary
1675+
{-# INLINE foldlWithKey #-}
1676+
1677+
-- | /O(n)/ Reduce the map by applying a function to each element
1678+
-- and combining the results with a monoid operation.
1679+
foldMapWithKey :: Monoid m => (k -> v -> m) -> HashMap k v -> m
1680+
foldMapWithKey f = go
1681+
where
1682+
go Empty = mempty
1683+
go (Leaf _ (L k v)) = f k v
1684+
go (BitmapIndexed _ ary) = A.foldMap go ary
1685+
go (Full ary) = A.foldMap go ary
1686+
go (Collision _ ary) = A.foldMap (\ (L k v) -> f k v) ary
1687+
{-# INLINE foldMapWithKey #-}
1688+
16221689
------------------------------------------------------------------------
16231690
-- * Filter
16241691

Data/HashMap/Lazy.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,15 @@ module Data.HashMap.Lazy
7070
, intersectionWithKey
7171

7272
-- * Folds
73+
, foldMapWithKey
74+
, foldr
75+
, foldl
76+
, foldr'
7377
, foldl'
78+
, foldrWithKey'
7479
, foldlWithKey'
75-
, foldr
7680
, foldrWithKey
81+
, foldlWithKey
7782

7883
-- * Filter
7984
, filter

Data/HashMap/Strict.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,15 @@ module Data.HashMap.Strict
6969
, intersectionWithKey
7070

7171
-- * Folds
72+
, foldMapWithKey
73+
, foldr
74+
, foldl
75+
, foldr'
7276
, foldl'
77+
, foldrWithKey'
7378
, foldlWithKey'
74-
, foldr
7579
, foldrWithKey
80+
, foldlWithKey
7681

7782
-- * Filter
7883
, filter

Data/HashMap/Strict/Base.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,15 @@ module Data.HashMap.Strict.Base
7171
, intersectionWithKey
7272

7373
-- * Folds
74+
, foldMapWithKey
75+
, foldr'
7476
, foldl'
77+
, foldrWithKey'
7578
, foldlWithKey'
7679
, HM.foldr
80+
, HM.foldl
7781
, foldrWithKey
82+
, foldlWithKey
7883

7984
-- * Filter
8085
, HM.filter

Data/HashSet/Base.hs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ module Data.HashSet.Base
5555
, intersection
5656

5757
-- * Folds
58-
, foldl'
5958
, foldr
59+
, foldr'
60+
, foldl
61+
, foldl'
6062

6163
-- * Filter
6264
, filter
@@ -77,15 +79,17 @@ module Data.HashSet.Base
7779

7880
import Control.DeepSeq (NFData(..))
7981
import Data.Data hiding (Typeable)
80-
import Data.HashMap.Base (HashMap, foldrWithKey, equalKeys, equalKeys1)
82+
import Data.HashMap.Base
83+
( HashMap, foldMapWithKey, foldlWithKey, foldrWithKey
84+
, equalKeys, equalKeys1)
8185
import Data.Hashable (Hashable(hashWithSalt))
8286
#if __GLASGOW_HASKELL__ >= 711
8387
import Data.Semigroup (Semigroup(..))
8488
#elif __GLASGOW_HASKELL__ < 709
8589
import Data.Monoid (Monoid(..))
8690
#endif
8791
import GHC.Exts (build)
88-
import Prelude hiding (filter, foldr, map, null)
92+
import Prelude hiding (filter, foldr, foldl, map, null)
8993
import qualified Data.Foldable as Foldable
9094
import qualified Data.HashMap.Base as H
9195
import qualified Data.List as List
@@ -138,16 +142,21 @@ instance Ord1 HashSet where
138142
#endif
139143

140144
instance Foldable.Foldable HashSet where
141-
foldr = Data.HashSet.Base.foldr
145+
foldMap f = foldMapWithKey (\a _ -> f a) . asMap
146+
foldr = foldr
142147
{-# INLINE foldr #-}
143-
foldl' = Data.HashSet.Base.foldl'
148+
foldl = foldl
149+
{-# INLINE foldl #-}
150+
foldl' = foldl'
144151
{-# INLINE foldl' #-}
152+
foldr' = foldr'
153+
{-# INLINE foldr' #-}
145154
#if MIN_VERSION_base(4,8,0)
146-
toList = Data.HashSet.Base.toList
155+
toList = toList
147156
{-# INLINE toList #-}
148-
null = Data.HashSet.Base.null
157+
null = null
149158
{-# INLINE null #-}
150-
length = Data.HashSet.Base.size
159+
length = size
151160
{-# INLINE length #-}
152161
#endif
153162

@@ -303,6 +312,16 @@ foldl' f z0 = H.foldlWithKey' g z0 . asMap
303312
where g z k _ = f z k
304313
{-# INLINE foldl' #-}
305314

315+
-- | /O(n)/ Reduce this set by applying a binary operator to all
316+
-- elements, using the given starting value (typically the
317+
-- right-identity of the operator). Each application of the operator
318+
-- is evaluated before before using the result in the next
319+
-- application. This function is strict in the starting value.
320+
foldr' :: (b -> a -> a) -> a -> HashSet b -> a
321+
foldr' f z0 = H.foldrWithKey' g z0 . asMap
322+
where g k _ z = f k z
323+
{-# INLINE foldr' #-}
324+
306325
-- | /O(n)/ Reduce this set by applying a binary operator to all
307326
-- elements, using the given starting value (typically the
308327
-- right-identity of the operator).
@@ -311,6 +330,14 @@ foldr f z0 = foldrWithKey g z0 . asMap
311330
where g k _ z = f k z
312331
{-# INLINE foldr #-}
313332

333+
-- | /O(n)/ Reduce this set by applying a binary operator to all
334+
-- elements, using the given starting value (typically the
335+
-- left-identity of the operator).
336+
foldl :: (a -> b -> a) -> a -> HashSet b -> a
337+
foldl f z0 = foldlWithKey g z0 . asMap
338+
where g z k _ = f z k
339+
{-# INLINE foldl #-}
340+
314341
-- | /O(n)/ Filter this set by retaining only elements satisfying a
315342
-- predicate.
316343
filter :: (a -> Bool) -> HashSet a -> HashSet a

0 commit comments

Comments
 (0)