Skip to content

Commit 8f4d1d3

Browse files
Support containers-0.8. (#18)
* Make `combineEq` match bias of `Set.from{Asc,Desc}List`. This commit provides a workaround for a change in the behaviour of `Set.from{Asc,Desc}List` w.r.t. duplicate elements, when upgrading from `containers-0.7` to `containers-0.8`. We can use the `K` type from `Tests.Util` to demonstrate this. With `containers-0.7`, `Set.from{Asc,Desc}List` chooses the **_first_** occurrence of any duplicated element: ```hs $ cabal repl nonempty-containers-test --constraint=containers==0.7 > import qualified Data.Set as Set > import Tests.Util > Set.fromAscList [K 'a' 1, K 'a' 2] fromList [K {getKX = 'a', getKY = 1}] > Set.fromDescList [K 'a' 1, K 'a' 2] fromList [K {getKX = 'a', getKY = 1}] ``` With `containers-0.8`, `Set.from{Asc,Desc}List` chooses the **_last_** occurrence of any duplicated element: ```hs $ cabal repl nonempty-containers-test --constraint=containers==0.8 > import qualified Data.Set as Set > import Tests.Util > Set.fromAscList [K 'a' 1, K 'a' 2] fromList [K {getKX = 'a', getKY = 2}] > Set.fromDescList [K 'a' 1, K 'a' 2] fromList [K {getKX = 'a', getKY = 2}] ``` This commit adjusts the bias of `Data.Set.NonEmpty.combineEq` to match the bias of `Set.from{Asc,Desc}List` in the version of `containers` being linked against. * Uncomment tests for `IntMap` traversal functions. * Fix traversal order of `traverseMapWithKey`. The current definition of `traverseMapWithKey` does not traverse keys in ascending order: ```hs > import qualified Data.IntMap as IM > import qualified Data.IntMap.NonEmpty.Internal as NEIM > kvs = [(i, i) | i <- [-2 .. 2]] > () <$ IM.traverseWithKey (curry print) (IM.fromList kvs) (-2,-2) (-1,-1) (0,0) (1,1) (2,2) > () <$ NEIM.traverseMapWithKey (curry print) (IM.fromList kvs) (-2,-2) (-1,-1) (2,2) (1,1) (0,0) ``` As a result, traversal functions for `Data.IntMap.NonEmpty` also do not traverse keys in ascending order. This commit redefines `traverseMapWithKey` to be a simple synonym of `Data.IntMap.traverseWithKey`. With this change, the test suite passes for all versions of containers from `0.6.3.1` up to `0.8` inclusively: - `0.6.3.1` - `0.6.4.1` - `0.6.5.1` - `0.6.6` - `0.6.7` - `0.6.8` - `0.7` - `0.8` * Inline definition of `traverseMapWithKey`. Since `traverseMapWithKey` is now a synonym of `IntMap.traverseWithKey`, we can inline its definition at all call sites. * Update bounds for `containers`. The lower bound of `0.6.3.1` is the earliest version for which all tests pass. This version of `containers` was released on 2020-07-14. This commit excludes older versions of `containers` up to and including version `0.6.2.1`, which was released on 2019-06-24. * Remove compatibility code for `containers < 0.5.11`. If the package itself is only buildable for `containers >= 0.6.3.1`, then this code is no longer reachable, and thus can be removed. * Inline compatibility functions. These functions are now just synonyms, so they can be safely inlined.
1 parent de9ba5c commit 8f4d1d3

File tree

13 files changed

+70
-261
lines changed

13 files changed

+70
-261
lines changed

nonempty-containers.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ library
5454
aeson
5555
, base >=4.9 && <5
5656
, comonad
57-
, containers >=0.5.9
57+
, containers >=0.6.3.1 && <0.9
5858
, deepseq
5959
, invariant
6060
, nonempty-vector
@@ -84,7 +84,7 @@ test-suite nonempty-containers-test
8484
build-depends:
8585
base >=4.9 && <5
8686
, comonad
87-
, containers >=0.5.9
87+
, containers >=0.6.3.1 && <0.9
8888
, hedgehog >=1.0
8989
, hedgehog-fn >=1.0
9090
, invariant

src/Data/IntMap/NonEmpty.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ lookupLT k (NEIntMap k0 v m) = case compare k k0 of
968968
lookupGT :: Key -> NEIntMap a -> Maybe (Key, a)
969969
lookupGT k (NEIntMap k0 v m) = case compare k k0 of
970970
LT -> Just (k0, v)
971-
EQ -> lookupMinMap m
971+
EQ -> M.lookupMin m
972972
GT -> M.lookupGT k m
973973
{-# INLINE lookupGT #-}
974974

@@ -1802,7 +1802,7 @@ findMin (NEIntMap k v _) = (k, v)
18021802
--
18031803
-- > findMax (fromList ((5,"a") :| [(3,"b")])) == (5,"a")
18041804
findMax :: NEIntMap a -> (Key, a)
1805-
findMax (NEIntMap k v m) = fromMaybe (k, v) . lookupMaxMap $ m
1805+
findMax (NEIntMap k v m) = fromMaybe (k, v) . M.lookupMax $ m
18061806
{-# INLINE findMax #-}
18071807

18081808
-- | /O(1)/. Delete the minimal key. Returns a potentially empty map

src/Data/IntMap/NonEmpty/Internal.hs

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,13 @@ module Data.IntMap.NonEmpty.Internal (
4646
traverseWithKey,
4747
traverseWithKey1,
4848
foldMapWithKey,
49-
traverseMapWithKey,
5049

5150
-- * Unsafe IntMap Functions
5251
insertMinMap,
5352
insertMaxMap,
5453

5554
-- * Debug
5655
valid,
57-
58-
-- * CPP compatibility
59-
lookupMinMap,
60-
lookupMaxMap,
6156
) where
6257

6358
import Control.Applicative
@@ -409,7 +404,7 @@ traverseWithKey ::
409404
traverseWithKey f (NEIntMap k v m0) =
410405
NEIntMap k
411406
<$> f k v
412-
<*> traverseMapWithKey f m0
407+
<*> M.traverseWithKey f m0
413408
{-# INLINE traverseWithKey #-}
414409

415410
-- | /O(n)/.
@@ -434,7 +429,7 @@ traverseWithKey1 f (NEIntMap k0 v m0) = case runMaybeApply m1 of
434429
Left m2 -> NEIntMap k0 <$> f k0 v <.> m2
435430
Right m2 -> flip (NEIntMap k0) m2 <$> f k0 v
436431
where
437-
m1 = traverseMapWithKey (\k -> MaybeApply . Left . f k) m0
432+
m1 = M.traverseWithKey (\k -> MaybeApply . Left . f k) m0
438433
{-# INLINEABLE traverseWithKey1 #-}
439434

440435
-- | /O(n)/. Convert the map to a non-empty list of key\/value pairs.
@@ -712,36 +707,3 @@ insertMinMap = M.insert
712707
insertMaxMap :: Key -> a -> IntMap a -> IntMap a
713708
insertMaxMap = M.insert
714709
{-# INLINEABLE insertMaxMap #-}
715-
716-
-- | /O(n)/. A fixed version of 'Data.IntMap.traverseWithKey' that
717-
-- traverses items in ascending order of keys.
718-
traverseMapWithKey :: Applicative t => (Key -> a -> t b) -> IntMap a -> t (IntMap b)
719-
traverseMapWithKey f = go
720-
where
721-
go Nil = pure Nil
722-
go (Tip k v) = Tip k <$> f k v
723-
go (Bin p m l r) = liftA2 (flip (Bin p m)) (go r) (go l)
724-
{-# INLINE traverseMapWithKey #-}
725-
726-
-- ---------------------------------------------
727-
728-
-- | CPP for new functions not in old containers
729-
-- ---------------------------------------------
730-
731-
-- | Compatibility layer for 'Data.IntMap.Lazy.lookupMinMap'.
732-
lookupMinMap :: IntMap a -> Maybe (Key, a)
733-
#if MIN_VERSION_containers(0,5,11)
734-
lookupMinMap = M.lookupMin
735-
#else
736-
lookupMinMap = fmap fst . M.minViewWithKey
737-
#endif
738-
{-# INLINE lookupMinMap #-}
739-
740-
-- | Compatibility layer for 'Data.IntMap.Lazy.lookupMaxMap'.
741-
lookupMaxMap :: IntMap a -> Maybe (Key, a)
742-
#if MIN_VERSION_containers(0,5,11)
743-
lookupMaxMap = M.lookupMax
744-
#else
745-
lookupMaxMap = fmap fst . M.maxViewWithKey
746-
#endif
747-
{-# INLINE lookupMaxMap #-}

src/Data/IntSet/NonEmpty.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,11 @@ disjoint ::
464464
Bool
465465
disjoint n1@(NEIntSet x1 s1) n2@(NEIntSet x2 s2) = case compare x1 x2 of
466466
-- x1 is not in n2
467-
LT -> s1 `disjointSet` toSet n2
467+
LT -> s1 `S.disjoint` toSet n2
468468
-- k1 and k2 are a part of the result
469469
EQ -> False
470470
-- k2 is not in n1
471-
GT -> toSet n1 `disjointSet` s2
471+
GT -> toSet n1 `S.disjoint` s2
472472
{-# INLINE disjoint #-}
473473

474474
-- | /O(m*log(n\/m + 1)), m <= n/. Difference of two sets.

src/Data/IntSet/NonEmpty/Internal.hs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
{-# LANGUAGE CPP #-}
21
{-# LANGUAGE DeriveDataTypeable #-}
32
{-# LANGUAGE ViewPatterns #-}
43
{-# OPTIONS_HADDOCK not-home #-}
@@ -29,7 +28,6 @@ module Data.IntSet.NonEmpty.Internal (
2928
valid,
3029
insertMinSet,
3130
insertMaxSet,
32-
disjointSet,
3331
) where
3432

3533
import Control.DeepSeq
@@ -274,17 +272,3 @@ insertMinSet = S.insert
274272
insertMaxSet :: Key -> IntSet -> IntSet
275273
insertMaxSet = S.insert
276274
{-# INLINEABLE insertMaxSet #-}
277-
278-
-- ---------------------------------------------
279-
280-
-- | CPP for new functions not in old containers
281-
-- ---------------------------------------------
282-
283-
-- | Comptability layer for 'Data.IntSet.disjoint'.
284-
disjointSet :: IntSet -> IntSet -> Bool
285-
#if MIN_VERSION_containers(0,5,11)
286-
disjointSet = S.disjoint
287-
#else
288-
disjointSet xs = S.null . S.intersection xs
289-
#endif
290-
{-# INLINE disjointSet #-}

src/Data/Sequence/NonEmpty.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ sortBy c (x :<|| xs) =
673673
sortOn :: Ord b => (a -> b) -> NESeq a -> NESeq a
674674
sortOn f (x :<|| xs) =
675675
withNonEmpty (singleton x) (insertOn f x)
676-
. sortOnSeq f
676+
. Seq.sortOn f
677677
$ xs
678678
{-# INLINE sortOn #-}
679679

@@ -721,7 +721,7 @@ unstableSortBy c = unsafeFromSeq . Seq.unstableSortBy c . toSeq
721721
-- TODO: figure out how to make it match 'Data.Sequence.unstableSortBy'
722722
-- without unsafe wrapping/unwrapping
723723
unstableSortOn :: Ord b => (a -> b) -> NESeq a -> NESeq a
724-
unstableSortOn f = unsafeFromSeq . unstableSortOnSeq f . toSeq
724+
unstableSortOn f = unsafeFromSeq . Seq.unstableSortOn f . toSeq
725725
-- unstableSortOn f (x :<|| xs) = withNonEmpty (singleton x) (insertOn f x)
726726
-- . Seq.unstableSortOn f
727727
-- $ xs
@@ -1051,7 +1051,7 @@ zipWith4 f (x :<|| xs) (y :<|| ys) (z :<|| zs) (r :<|| rs) = f x y z r :<|| Seq.
10511051
-- calculating the sequence of pairs and using 'fmap' to extract each
10521052
-- component sequence.
10531053
unzipWith :: (a -> (b, c)) -> NESeq a -> (NESeq b, NESeq c)
1054-
unzipWith f (x :<|| xs) = bimap (y :<||) (z :<||) . unzipWithSeq f $ xs
1054+
unzipWith f (x :<|| xs) = bimap (y :<||) (z :<||) . Seq.unzipWith f $ xs
10551055
where
10561056
~(y, z) = f x
10571057
{-# NOINLINE [1] unzipWith #-}

src/Data/Sequence/NonEmpty/Internal.hs

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,6 @@ module Data.Sequence.NonEmpty.Internal (
4242
zip,
4343
zipWith,
4444
unzip,
45-
sortOnSeq,
46-
unstableSortOnSeq,
47-
unzipSeq,
48-
unzipWithSeq,
4945
) where
5046

5147
import Control.Comonad
@@ -402,7 +398,7 @@ zipWith f (x :<|| xs) (y :<|| ys) = f x y :<|| Seq.zipWith f xs ys
402398
--
403399
-- See the note about efficiency at 'Data.Sequence.NonEmpty.unzipWith'.
404400
unzip :: NESeq (a, b) -> (NESeq a, NESeq b)
405-
unzip ((x, y) :<|| xys) = bimap (x :<||) (y :<||) . unzipSeq $ xys
401+
unzip ((x, y) :<|| xys) = bimap (x :<||) (y :<||) . Seq.unzip $ xys
406402
{-# INLINE unzip #-}
407403

408404
instance Semigroup (NESeq a) where
@@ -572,53 +568,3 @@ mfixSeq f = fromFunction (length (f err)) (\k -> fix (\xk -> f xk `index` k))
572568

573569
instance NFData a => NFData (NESeq a) where
574570
rnf (x :<|| xs) = rnf x `seq` rnf xs
575-
576-
-- ---------------------------------------------
577-
578-
-- | CPP for new functions not in old containers
579-
-- ---------------------------------------------
580-
581-
-- | Compatibility layer for 'Data.Sequence.sortOn'.
582-
sortOnSeq :: Ord b => (a -> b) -> Seq a -> Seq a
583-
#if MIN_VERSION_containers(0,5,11)
584-
sortOnSeq = Seq.sortOn
585-
#else
586-
sortOnSeq f = Seq.sortBy (\x y -> f x `compare` f y)
587-
#endif
588-
{-# INLINE sortOnSeq #-}
589-
590-
-- | Compatibility layer for 'Data.Sequence.unstableSortOn'.
591-
unstableSortOnSeq :: Ord b => (a -> b) -> Seq a -> Seq a
592-
#if MIN_VERSION_containers(0,5,11)
593-
unstableSortOnSeq = Seq.unstableSortOn
594-
#else
595-
unstableSortOnSeq f = Seq.unstableSortBy (\x y -> f x `compare` f y)
596-
#endif
597-
{-# INLINE unstableSortOnSeq #-}
598-
599-
-- | Compatibility layer for 'Data.Sequence.unzip'.
600-
unzipSeq :: Seq (a, b) -> (Seq a, Seq b)
601-
#if MIN_VERSION_containers(0,5,11)
602-
unzipSeq = Seq.unzip
603-
{-# INLINE unzipSeq #-}
604-
#else
605-
unzipSeq = \case
606-
(x, y) :<| xys -> bimap (x :<|) (y :<|) . unzipSeq $ xys
607-
Empty -> (Empty, Empty)
608-
{-# INLINABLE unzipSeq #-}
609-
#endif
610-
611-
-- | Compatibility layer for 'Data.Sequence.unzipWith'.
612-
unzipWithSeq :: (a -> (b, c)) -> Seq a -> (Seq b, Seq c)
613-
#if MIN_VERSION_containers(0,5,11)
614-
unzipWithSeq = Seq.unzipWith
615-
{-# INLINE unzipWithSeq #-}
616-
#else
617-
unzipWithSeq f = go
618-
where
619-
go = \case
620-
x :<| xs -> let ~(y, z) = f x
621-
in bimap (y :<|) (z :<|) . go $ xs
622-
Empty -> (Empty, Empty)
623-
{-# INLINABLE unzipWithSeq #-}
624-
#endif

src/Data/Set/NonEmpty.hs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE CPP #-}
23
{-# LANGUAGE PatternSynonyms #-}
34
{-# LANGUAGE ScopedTypeVariables #-}
45
{-# LANGUAGE TupleSections #-}
@@ -337,7 +338,7 @@ powerSet (NESet x s0) = case nonEmptySet p1 of
337338
where
338339
-- powerset should never be empty
339340
p0 :: NESet (Set a)
340-
p0@(NESet _ p0s) = forSure $ powerSetSet s0
341+
p0@(NESet _ p0s) = forSure $ S.powerSet s0
341342
p1 :: Set (NESet a)
342343
p1 = S.mapMonotonic forSure p0s -- only minimal element is empty, so the rest aren't
343344
forSure =
@@ -462,11 +463,11 @@ disjoint ::
462463
Bool
463464
disjoint n1@(NESet x1 s1) n2@(NESet x2 s2) = case compare x1 x2 of
464465
-- x1 is not in n2
465-
LT -> s1 `disjointSet` toSet n2
466+
LT -> s1 `S.disjoint` toSet n2
466467
-- k1 and k2 are a part of the result
467468
EQ -> False
468469
-- k2 is not in n1
469-
GT -> toSet n1 `disjointSet` s2
470+
GT -> toSet n1 `S.disjoint` s2
470471
{-# INLINE disjoint #-}
471472

472473
-- | /O(m*log(n\/m + 1)), m <= n/. Difference of two sets.
@@ -565,7 +566,7 @@ disjointUnion ::
565566
disjointUnion (NESet x1 s1) n2 =
566567
NESet
567568
(Left x1)
568-
(s1 `disjointUnionSet` toSet n2)
569+
(s1 `S.disjointUnion` toSet n2)
569570
{-# INLINE disjointUnion #-}
570571

571572
-- | /O(n)/. Filter all elements that satisfy the predicate.
@@ -1054,5 +1055,9 @@ combineEq (x :| xs) = go x xs
10541055
where
10551056
go z [] = z :| []
10561057
go z (y : ys)
1058+
#if MIN_VERSION_containers(0,8,0)
1059+
| z == y = go y ys
1060+
#else
10571061
| z == y = go z ys
1062+
#endif
10581063
| otherwise = z NE.<| go y ys

0 commit comments

Comments
 (0)