Skip to content

Commit 5d4bc2e

Browse files
authored
Fix Issue 1004: effect order in filterAMissing (#1005)
The order of Applicative effects in filterAMissing was incorrect, causing the order of effects to differ from key order and be influenced by how the binary tree was balanced. The fix is to arrange that effects arising from the key and value at an internal node come after those in its left branch instead of before. (Regardless of this fix such effects come before those effects arising from the right branch.) This change also expands test coverage to detect a regression of this fix.
1 parent b2a54c8 commit 5d4bc2e

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

containers-tests/tests/map-properties.hs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,13 +1212,49 @@ prop_mergeWithKeyModel xs ys
12121212
-- This uses the instance
12131213
-- Monoid a => Applicative ((,) a)
12141214
-- to test that effects are sequenced in ascending key order.
1215-
prop_mergeA_effects :: UMap -> UMap -> Property
1216-
prop_mergeA_effects xs ys
1215+
prop_mergeA_effects :: WhenMissingSpec -> WhenMissingSpec -> WhenMatchedSpec -> UMap -> UMap -> Property
1216+
prop_mergeA_effects onlyLeft onlyRight both xs ys
12171217
= effects === sort effects
12181218
where
1219-
(effects, _m) = mergeA whenMissing whenMissing whenMatched xs ys
1220-
whenMissing = traverseMissing (\k _ -> ([k], ()))
1221-
whenMatched = zipWithAMatched (\k _ _ -> ([k], ()))
1219+
(effects, _m) = mergeA (whenMissing onlyLeft) (whenMissing onlyRight) (whenMatched both) xs ys
1220+
whenMissing spec = case spec of
1221+
DropMissing -> dropMissing
1222+
PreserveMissing -> preserveMissing
1223+
FilterMissing -> filterMissing (\_ _ -> False)
1224+
FilterAMissing -> filterAMissing (\k _ -> ([k], False))
1225+
MapMissing -> mapMissing (\_ _ -> ())
1226+
TraverseMissing -> traverseMissing (\k _ -> ([k], ()))
1227+
MapMaybeMissing -> mapMaybeMissing (\_ _ -> Nothing)
1228+
TraverseMaybeMissing -> traverseMaybeMissing (\k _ -> ([k], Nothing))
1229+
whenMatched spec = case spec of
1230+
ZipWithMatched -> zipWithMatched (\_ _ _ -> ())
1231+
ZipWithAMatched -> zipWithAMatched (\k _ _ -> ([k], ()))
1232+
ZipWithMaybeMatched -> zipWithMaybeMatched (\_ _ _ -> Nothing)
1233+
ZipWithMaybeAMatched -> zipWithMaybeAMatched (\k _ _ -> ([k], Nothing))
1234+
1235+
data WhenMissingSpec
1236+
= DropMissing
1237+
| PreserveMissing
1238+
| FilterMissing
1239+
| FilterAMissing
1240+
| MapMissing
1241+
| TraverseMissing
1242+
| MapMaybeMissing
1243+
| TraverseMaybeMissing
1244+
deriving (Bounded, Enum, Show)
1245+
1246+
instance Arbitrary WhenMissingSpec where
1247+
arbitrary = arbitraryBoundedEnum
1248+
1249+
data WhenMatchedSpec
1250+
= ZipWithMatched
1251+
| ZipWithAMatched
1252+
| ZipWithMaybeMatched
1253+
| ZipWithMaybeAMatched
1254+
deriving (Bounded, Enum, Show)
1255+
1256+
instance Arbitrary WhenMatchedSpec where
1257+
arbitrary = arbitraryBoundedEnum
12221258

12231259
----------------------------------------------------------------
12241260

containers/src/Data/Map/Internal.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2891,12 +2891,12 @@ filterWithKey p t@(Bin _ kx x l r)
28912891
filterWithKeyA :: Applicative f => (k -> a -> f Bool) -> Map k a -> f (Map k a)
28922892
filterWithKeyA _ Tip = pure Tip
28932893
filterWithKeyA p t@(Bin _ kx x l r) =
2894-
liftA3 combine (p kx x) (filterWithKeyA p l) (filterWithKeyA p r)
2894+
liftA3 combine (filterWithKeyA p l) (p kx x) (filterWithKeyA p r)
28952895
where
2896-
combine True pl pr
2896+
combine pl True pr
28972897
| pl `ptrEq` l && pr `ptrEq` r = t
28982898
| otherwise = link kx x pl pr
2899-
combine False pl pr = link2 pl pr
2899+
combine pl False pr = link2 pl pr
29002900

29012901
-- | \(O(\log n)\). Take while a predicate on the keys holds.
29022902
-- The user is responsible for ensuring that for all keys @j@ and @k@ in the map,

0 commit comments

Comments
 (0)