packages feed

insert-ordered-containers 0.3.0 → 0.3.0.1

raw patch · 6 files changed

+49/−44 lines, 6 filesdep +containersPVP ok

version bump matches the API change (PVP)

Dependencies added: containers

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,3 +1,6 @@+- 0.3.0.1+    - Fix quadratic behavior in traverseWithKey/for+ - 0.3.0     - Fix ordering when decoding from JSON (with tests)       https://github.com/erikd/insert-ordered-containers/issues/7
insert-ordered-containers.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               insert-ordered-containers-version:            0.3.0+version:            0.3.0.1 synopsis:   Associative containers retaining insertion order for traversals. @@ -45,6 +45,7 @@   build-depends:     , aeson                 >=2.2.3.0  && <2.3     , base                  >=4.12.0.0 && <4.23+    , containers            >=0.6.0.1     , deepseq               >=1.4.4.0  && <1.6     , hashable              >=1.4.7.0  && <1.6     , indexed-traversable   >=0.1.4    && <0.2
src/Data/HashMap/InsOrd/Internal.hs view
@@ -1,41 +1,36 @@-{-# LANGUAGE GADTs #-} module Data.HashMap.InsOrd.Internal where  import Prelude hiding (filter, foldr, lookup, map, null) -import Control.Applicative ((<**>))+import Data.List (sortBy)+import Data.Ord  (comparing) +import           Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap+import qualified Data.IntMap.Strict  as IntMap+ ---------------------------------------------------------------------------------- SortedAp+-- Ordered traversal ------------------------------------------------------------------------------- --- Sort using insertion sort--- Hopefully it's fast enough for where we need it--- otherwise: https://gist.github.com/treeowl/9621f58d55fe0c4f9162be0e074b1b29--- http://elvishjerricco.github.io/2017/03/23/applicative-sorting.html also related---- Free applicative which re-orders effects--- Mostly from Edward Kmett's `free` package.-data SortedAp f a where-    Pure :: a -> SortedAp f a-    SortedAp   :: !Int -> f a -> SortedAp f (a -> b) -> SortedAp f b--instance Functor (SortedAp f) where-    fmap f (Pure a)   = Pure (f a)-    fmap f (SortedAp i x y)   = SortedAp i x ((f .) <$> y)--instance Applicative (SortedAp f) where-    pure = Pure-    Pure f <*> y = fmap f y-    -- This is different from real Ap-    f <*> Pure y = fmap ($ y) f-    f@(SortedAp i x y) <*> z@(SortedAp j u v)-        | i < j     = SortedAp i x (flip <$> y <*> z)-        | otherwise = SortedAp j u ((.) <$> f <*> v)--liftSortedAp :: Int -> f a -> SortedAp f a-liftSortedAp i x = SortedAp i x (Pure id)+-- | Traverse a 'HashMap', sequencing effects in ascending order of the given+-- index rather than in the map's internal order. The result has the same+-- keys as the input. The index must be distinct for every entry.+--+-- O(n log n): sort the entries by index and traverse that list, then look+-- each result up by its index. (This replaces an insertion-sorting free+-- applicative, which was O(n^2).)+traverseWithKeyOrderedBy+    :: Applicative f+    => (a -> Int) -> (k -> a -> f b) -> HashMap k a -> f (HashMap k b)+traverseWithKeyOrderedBy ix f m =+    refill <$> traverse g (sortBy (comparing fst) [(ix a, (k, a)) | (k, a) <- HashMap.toList m])+  where+    g (i, (k, a)) = (,) i <$> f k a -retractSortedAp :: Applicative f => SortedAp f a -> f a-retractSortedAp (Pure x) = pure x-retractSortedAp (SortedAp _ f x) = f <**> retractSortedAp x+    -- Results come back in ascending index order. We reuse m's structure rather+    -- than rebuilding with 'fromList', since that would need 'Hashable k', which+    -- the 'Traversable' instance can't require.+    refill rs =+        let im = IntMap.fromDistinctAscList rs+        in HashMap.map (\a -> im IntMap.! ix a) m
src/Data/HashMap/Strict/InsOrd.hs view
@@ -482,8 +482,8 @@ foldMapWithKey f = foldMap (uncurry f) . toList  traverseWithKey :: Applicative f => (k -> a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)-traverseWithKey f (InsOrdHashMap n m) = InsOrdHashMap n <$> retractSortedAp-    (HashMap.traverseWithKey (\k (P i v) -> liftSortedAp i (P i <$> f k v)) m)+traverseWithKey f (InsOrdHashMap n m) = InsOrdHashMap n <$>+    traverseWithKeyOrderedBy getPK (\k (P i v) -> P i <$> f k v) m  ------------------------------------------------------------------------------- -- Unordered@@ -636,15 +636,10 @@ -- Internal ------------------------------------------------------------------------------- --- TODO: more efficient way is to do two traversals--- - collect the indexes--- - pack the indexes (Map old new)--- - traverse second time, changing the indexes fromHashMapP :: HashMap k (P v) -> InsOrdHashMap k v-fromHashMapP = mk . flip runState 0 . retractSortedAp . traverse f+fromHashMapP = mk . flip runState 0 . traverseWithKeyOrderedBy getPK (\_ (P _ v) -> newP v)   where     mk (m, i) = InsOrdHashMap i m-    f (P i v) = liftSortedAp i (newP v)  -- | Test if the internal map structure is valid. valid :: InsOrdHashMap k v -> Bool
src/Data/HashSet/InsOrd.hs view
@@ -43,7 +43,7 @@     )where  import Prelude-       (Bool, Eq ((==)), Int, Maybe (..), const, flip, fmap, fst,+       (Bool, Eq ((==)), Int, Maybe (..), const, flip, fmap, fst, id,        maybe, otherwise, return, snd, ($), (&&), (+), (.), (<$), (<$>), (<),        (>), (>=), (||)) @@ -309,10 +309,9 @@ -------------------------------------------------------------------------------  fromHashMapInt :: HashMap k Int -> InsOrdHashSet k-fromHashMapInt = mk . flip runState 0 . retractSortedAp . traverse f+fromHashMapInt = mk . flip runState 0 . traverseWithKeyOrderedBy id (\_ _ -> newInt')   where     mk (m, i) = InsOrdHashSet i m-    f i = liftSortedAp i newInt'  newInt :: a -> State Int (a, Int) newInt a = state $ \s -> ((a, s), s + 1)
test/Tests.hs view
@@ -30,6 +30,7 @@         , testProperty "aeson roundtrip" $ aesonRoundtrip         , testProperty "show . read = id" showReadRoundtrip         , testProperty "Eq respects ordering" eqRespectsOrdering+        , testProperty "traverseWithKey effects in insertion order" traverseWithKeyOrder         ]     , testGroup "Regressions"         [ testProperty "issue 10: union overflow" $ issue10@@ -150,11 +151,22 @@     rhs = Just iom     lhs = readMaybe $ show iom + eqRespectsOrdering :: Operation Int Int -> Property eqRespectsOrdering op = iom == iomRev ==> InsOrd.toList iom === InsOrd.toList iomRev   where     iom = evalOpInsOrd op     iomRev = InsOrd.fromList . reverse . InsOrd.toList $ iom++traverseWithKeyOrder :: Operation Word8 Int -> Property+traverseWithKeyOrder op =+    -- effects (which just accumulate key/value pairs here) ran in order:+    effects === InsOrd.toList iom+    .&&. InsOrd.toList iom' === [ (k, v + 1) | (k, v) <- InsOrd.toList iom ]+    .&&. property (InsOrd.valid iom')+  where+    iom = evalOpInsOrd op+    (effects, iom') = InsOrd.traverseWithKey (\k v -> ([(k, v)], v + 1)) iom  ------------------------------------------------------------------------------- -- Regressions