Skip to content
This repository was archived by the owner on Oct 4, 2020. It is now read-only.

Commit 2c42ac7

Browse files
committed
Faster & simpler traverse for StrMap
1 parent c1a826b commit 2c42ac7

File tree

2 files changed

+28
-3
lines changed

2 files changed

+28
-3
lines changed

src/Data/StrMap.purs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Data.StrMap
1313
, insert
1414
, lookup
1515
, toUnfoldable
16+
, toAscUnfoldable
1617
, fromFoldable
1718
, fromFoldableWith
1819
, delete
@@ -50,7 +51,7 @@ import Data.Maybe (Maybe(..), maybe, fromMaybe)
5051
import Data.Monoid (class Monoid, mempty)
5152
import Data.StrMap.ST as SM
5253
import Data.Traversable (class Traversable, traverse)
53-
import Data.Tuple (Tuple(..), uncurry)
54+
import Data.Tuple (Tuple(..), fst)
5455
import Data.Unfoldable (class Unfoldable)
5556

5657
-- | `StrMap a` represents a map from `String`s to values of type `a`.
@@ -108,7 +109,7 @@ instance foldableStrMap :: Foldable StrMap where
108109
foldMap f = foldMap (const f)
109110

110111
instance traversableStrMap :: Traversable StrMap where
111-
traverse f ms = foldr (\x acc -> union <$> x <*> acc) (pure empty) ((map (uncurry singleton)) <$> (traverse f <$> toArray ms))
112+
traverse f ms = fold (\acc k v -> insert k <$> f v <*> acc) (pure empty) ms
112113
sequence = traverse id
113114

114115
-- Unfortunately the above are not short-circuitable (consider using purescript-machines)
@@ -215,6 +216,11 @@ foreign import _collect :: forall a b . (String -> a -> b) -> StrMap a -> Array
215216
toUnfoldable :: forall f a. Unfoldable f => StrMap a -> f (Tuple String a)
216217
toUnfoldable = A.toUnfoldable <<< _collect Tuple
217218

219+
-- | Unfolds a map into a list of key/value pairs which is guaranteed to be
220+
-- | sorted by key
221+
toAscUnfoldable :: forall f a. Unfoldable f => StrMap a -> f (Tuple String a)
222+
toAscUnfoldable = A.toUnfoldable <<< A.sortWith fst <<< _collect Tuple
223+
218224
-- Internal
219225
toArray :: forall a. StrMap a -> Array (Tuple String a)
220226
toArray = _collect Tuple

test/Test/Data/StrMap.purs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import Data.List.NonEmpty as NEL
1515
import Data.Maybe (Maybe(..))
1616
import Data.NonEmpty ((:|))
1717
import Data.StrMap as M
18-
import Data.Tuple (Tuple(..), fst)
18+
import Data.Tuple (Tuple(..), fst, uncurry)
19+
import Data.Traversable (traverse, sequence)
1920

2021
import Partial.Unsafe (unsafePartial)
2122

@@ -28,6 +29,11 @@ newtype TestStrMap v = TestStrMap (M.StrMap v)
2829
instance arbTestStrMap :: (Arbitrary v) => Arbitrary (TestStrMap v) where
2930
arbitrary = TestStrMap <<< (M.fromFoldable :: L.List (Tuple String v) -> M.StrMap v) <$> arbitrary
3031

32+
newtype SmallArray v = SmallArray (Array v)
33+
34+
instance arbSmallArray :: (Arbitrary v) => Arbitrary (SmallArray v) where
35+
arbitrary = SmallArray <$> Gen.resize 3 arbitrary
36+
3137
data Instruction k v = Insert k v | Delete k
3238

3339
instance showInstruction :: (Show k, Show v) => Show (Instruction k v) where
@@ -54,6 +60,14 @@ runInstructions instrs t0 = foldl step t0 instrs
5460
number :: Int -> Int
5561
number n = n
5662

63+
oldTraverse :: forall a b m. Applicative m => (a -> m b) -> M.StrMap a -> m (M.StrMap b)
64+
oldTraverse f ms = A.foldr (\x acc -> M.union <$> x <*> acc) (pure M.empty) ((map (uncurry M.singleton)) <$> (traverse f <$> (M.toUnfoldable ms :: Array (Tuple String a))))
65+
oldSequence :: forall a m. Applicative m => M.StrMap (m a) -> m (M.StrMap a)
66+
oldSequence = oldTraverse id
67+
68+
toAscArray :: forall a. M.StrMap a -> Array (Tuple String a)
69+
toAscArray = M.toAscUnfoldable
70+
5771
strMapTests :: forall eff. Eff (console :: CONSOLE, random :: RANDOM, exception :: EXCEPTION | eff) Unit
5872
strMapTests = do
5973
log "Test inserting into empty tree"
@@ -167,6 +181,11 @@ strMapTests = do
167181
resultViaLists = m # M.toUnfoldable # map (\(Tuple k v) → Tuple k (f k v)) # (M.fromFoldable :: forall a. L.List (Tuple String a) -> M.StrMap a)
168182
in resultViaMapWithKey === resultViaLists
169183

184+
log "sequence gives the same results as an old version (up to ordering)"
185+
quickCheck \(TestStrMap mOfSmallArrays :: TestStrMap (SmallArray Int)) ->
186+
let m = (\(SmallArray a) -> a) <$> mOfSmallArrays
187+
in A.sort (toAscArray <$> oldSequence m) === A.sort (toAscArray <$> sequence m)
188+
170189
log "Bug #63: accidental observable mutation in foldMap"
171190
quickCheck \(TestStrMap m) ->
172191
let lhs = go m

0 commit comments

Comments
 (0)