packages feed

insert-ordered-containers 0.2.0.0 → 0.2.1.0

raw patch · 4 files changed

+167/−34 lines, 4 filesdep ~QuickCheckdep ~aesondep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: QuickCheck, aeson, base, base-compat, lens, semigroupoids, tasty, tasty-quickcheck

API changes (from Hackage documentation)

- Data.HashMap.Strict.InsOrd: instance (GHC.Classes.Eq k, Data.Data.Data k, Data.Data.Data v, Data.Hashable.Class.Hashable k) => Data.Data.Data (Data.HashMap.Strict.InsOrd.InsOrdHashMap k v)
+ Data.HashMap.Strict.InsOrd: foldMapWithKey :: Monoid m => (k -> a -> m) -> InsOrdHashMap k a -> m
+ Data.HashMap.Strict.InsOrd: instance (Data.Hashable.Class.Hashable k, GHC.Classes.Eq k, Data.Data.Data v, Data.Data.Data k) => Data.Data.Data (Data.HashMap.Strict.InsOrd.InsOrdHashMap k v)
+ Data.HashMap.Strict.InsOrd: instance GHC.Base.Applicative (Data.HashMap.Strict.InsOrd.SortedAp f)
+ Data.HashMap.Strict.InsOrd: instance GHC.Base.Functor (Data.HashMap.Strict.InsOrd.SortedAp f)
+ Data.HashMap.Strict.InsOrd: unorderedFoldMap :: Monoid m => (a -> m) -> InsOrdHashMap k a -> m
+ Data.HashMap.Strict.InsOrd: unorderedFoldMapWithKey :: Monoid m => (k -> a -> m) -> InsOrdHashMap k a -> m
+ Data.HashMap.Strict.InsOrd: unorderedTraverse :: Applicative f => (a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)
+ Data.HashMap.Strict.InsOrd: unorderedTraverseWithKey :: Applicative f => (k -> a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)

Files

CHANGELOG.md view
@@ -1,3 +1,12 @@+- 0.2.1.0+    - Fix `Traversable`, `TraversableWithIndex`, `FoldableWithIndex` to traverse+      in insertion order+      ([#12](https://github.com/phadej/insert-ordered-containers/issues/12))+    - Add `unorderedTraverse`, `unorderedTraverseWithKey`, `unoderedFoldMap`, and+      `unorderedFoldMapWithKey`.+    - `union` doesn't overflow the internal counter+      ([#10](https://github.com/phadej/insert-ordered-containers/issues/10))+ - 0.2.0.0     - Use `aeson-1`     - removed our `FromJSONKey` and `ToJSONKey` in favour of `aeson` variants
insert-ordered-containers.cabal view
@@ -1,9 +1,5 @@--- This file has been generated from package.yaml by hpack version 0.14.0.------ see: https://github.com/sol/hpack- name:           insert-ordered-containers-version:        0.2.0.0+version:        0.2.1.0 synopsis:       Associative containers retating insertion order for traversals. description:    Associative containers retating insertion order for traversals. category:       Web@@ -31,10 +27,10 @@   ghc-options: -Wall   build-depends:       base                  >=4.6      && <4.10-    , aeson                 >=1.0.0.0  && <1.1+    , aeson                 >=1.0.0.0  && <1.2     , base-compat           >=0.6.0    && <0.10     , hashable              >=1.2.3.3  && <1.4-    , lens                  >=4.7      && <4.15+    , lens                  >=4.7      && <4.16     , semigroupoids         >=4.3      && <5.2     , semigroups            >=0.16.2.2 && <0.19     , text                  >=1.2.0.6  && <1.3@@ -51,19 +47,19 @@       test   ghc-options: -Wall   build-depends:-      base                  >=4.6      && <4.10-    , aeson                 >=1.0.0.0  && <1.1-    , base-compat           >=0.6.0    && <0.10-    , hashable              >=1.2.3.3  && <1.4-    , lens                  >=4.7      && <4.15-    , semigroupoids         >=4.3      && <5.2-    , semigroups            >=0.16.2.2 && <0.19-    , text                  >=1.2.0.6  && <1.3-    , transformers          >=0.3.0.0  && <0.6-    , unordered-containers  >=0.2.7.0  && <0.3+      base+    , aeson+    , base-compat+    , hashable+    , lens+    , semigroupoids+    , semigroups+    , text+    , transformers+    , unordered-containers     , base     , insert-ordered-containers     , tasty             >= 0.10.1.2 && <0.12     , tasty-quickcheck  >= 0.8.3.2  && <0.9-    , QuickCheck        >=2.7.6     && <2.9+    , QuickCheck        >=2.7.6     && <2.10   default-language: Haskell2010
src/Data/HashMap/Strict/InsOrd.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE DeriveFunctor         #-} {-# LANGUAGE DeriveTraversable     #-} {-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE Trustworthy           #-}@@ -39,6 +40,9 @@     traverseKeys,     mapWithKey,     traverseWithKey,+    -- ** Unordered+    unorderedTraverse,+    unorderedTraverseWithKey,     -- * Difference and intersection     difference,     intersection,@@ -49,6 +53,10 @@     foldlWithKey',     foldr,     foldrWithKey,+    foldMapWithKey,+    -- ** Unordered+    unorderedFoldMap,+    unorderedFoldMapWithKey,     -- * Filter     filter,     filterWithKey,@@ -76,6 +84,7 @@ import Prelude        () import Prelude.Compat hiding (filter, foldr, lookup, map, null) +import           Control.Applicative             (Const (..), (<**>)) import           Control.Arrow                   (first, second) import           Data.Aeson import qualified Data.Aeson.Encoding             as E@@ -92,8 +101,8 @@ import           Text.Read                       (Lexeme (..), Read (..), lexP,                                                   parens, readListPrecDefault) -import Control.Lens                     (At (..), FoldableWithIndex,-                                         FunctorWithIndex, Index, Iso, IxValue,+import Control.Lens                     (At (..), FoldableWithIndex (..),+                                         FunctorWithIndex (..), Index, Iso, IxValue,                                          Ixed (..), TraversableWithIndex (..),                                          Traversal, iso, (<&>), _1, _2) import Control.Monad.Trans.State.Strict (State, runState, state)@@ -180,8 +189,7 @@ #endif  instance Traversable (InsOrdHashMap k) where-    traverse f (InsOrdHashMap i m) =-        InsOrdHashMap i <$> (traverse . traverse) f m+    traverse f m = traverseWithKey (\_ -> f) m  instance (Eq k, Hashable k) => Apply (InsOrdHashMap k) where     (<.>) = intersectionWith id@@ -248,8 +256,10 @@       where mv = lookup k m     {-# INLINABLE at #-} -instance (Eq k, Hashable k) => FunctorWithIndex k (InsOrdHashMap k)-instance (Eq k, Hashable k) => FoldableWithIndex k (InsOrdHashMap k)+instance (Eq k, Hashable k) => FunctorWithIndex k (InsOrdHashMap k) where+    imap = mapWithKey+instance (Eq k, Hashable k) => FoldableWithIndex k (InsOrdHashMap k) where+    ifoldMap = foldMapWithKey instance (Eq k, Hashable k) => TraversableWithIndex k (InsOrdHashMap k) where     itraverse = traverseWithKey @@ -351,8 +361,11 @@     => (v -> v -> v)     -> InsOrdHashMap k v -> InsOrdHashMap k v -> InsOrdHashMap k v unionWith f (InsOrdHashMap i a) (InsOrdHashMap j b) =-    InsOrdHashMap (i + j) $ HashMap.unionWith f' a b'+    mk $ HashMap.unionWith f' a b'   where+    -- the threshold is arbitrary, it meant to amortise need for packing of indices+    mk | i > 0xfffff || j >= 0xfffff = fromHashMapP+       | otherwise                   = InsOrdHashMap (i + j)     b' = fmap (incPK i) b     f' (P ii x) (P _ y) = P ii (f x y) @@ -400,8 +413,65 @@   where     f' k (P j x) = P j (f k x) +foldMapWithKey :: Monoid m => (k -> a -> m) -> InsOrdHashMap k a -> m+foldMapWithKey f = foldMap (uncurry f) . toList+ traverseWithKey :: Applicative f => (k -> a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)-traverseWithKey f (InsOrdHashMap i m) =+traverseWithKey f (InsOrdHashMap n m) = InsOrdHashMap n <$> retractSortedAp+    (HashMap.traverseWithKey (\k (P i v) -> liftSortedAp i (P i <$> f k v)) m)++-- 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)++retractSortedAp :: Applicative f => SortedAp f a -> f a+retractSortedAp (Pure x) = pure x+retractSortedAp (SortedAp _ f x) = f <**> retractSortedAp x++-------------------------------------------------------------------------------+-- Unordered+-------------------------------------------------------------------------------++-- | More efficient than 'foldMap', when folding in insertion order is not important.+unorderedFoldMap :: Monoid m => (a -> m) -> InsOrdHashMap k a -> m+unorderedFoldMap f (InsOrdHashMap _ m) = foldMap (f . getPV) m++-- | More efficient than 'foldMapWithKey', when folding in insertion order is not important.+unorderedFoldMapWithKey :: Monoid m => (k -> a -> m) -> InsOrdHashMap k a -> m+unorderedFoldMapWithKey f m =+    getConst (unorderedTraverseWithKey (\k a -> Const (f k a)) m)++-- | More efficient than 'traverse', when traversing in insertion order is not important.+unorderedTraverse :: Applicative f => (a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)+unorderedTraverse f (InsOrdHashMap i m) =+    InsOrdHashMap i <$> (traverse . traverse) f m++-- | More efficient than `traverseWithKey`, when traversing in insertion order is not important.+unorderedTraverseWithKey :: Applicative f => (k -> a -> f b) -> InsOrdHashMap k a -> f (InsOrdHashMap k b)+unorderedTraverseWithKey f (InsOrdHashMap i m) =     InsOrdHashMap i <$> HashMap.traverseWithKey f' m   where     f' k (P j x) = P j <$> f k x@@ -531,6 +601,16 @@ ------------------------------------------------------------------------------- -- 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+  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
test/Tests.hs view
@@ -3,10 +3,12 @@ import Prelude        () import Prelude.Compat +import Control.Lens   (folded, ifolded, (^..), (^@..)) import Data.Function  (on) import Data.Hashable  (Hashable (..)) import Data.List      (nubBy) import Data.Semigroup ((<>))+import Data.Traversable (foldMapDefault) import Data.Word      (Word8) import Text.Read      (readMaybe) @@ -19,14 +21,23 @@ import Test.Tasty.QuickCheck  main :: IO ()-main = defaultMain $ testGroup "Properties" $-    [ testProperty "toList . fromList ~= id" $ toListFromList-    , testProperty "toList distributes over mappend" $ toListMappendDistribute-    , testProperty "behaves like HashMap" $ operationModel-    , testProperty "valid" $ validProperty-    , testProperty "Hashable agree" $ hashableProperty-    , testProperty "aeson roundtrip" $ aesonRoundtrip-    , testProperty "show . read = id" showReadRoundtrip+main = defaultMain $ testGroup "tests"+    [ testGroup "Properties" $+        [ testProperty "toList . fromList ~= id" $ toListFromList+        , testProperty "toList distributes over mappend" $ toListMappendDistribute+        , testProperty "behaves like HashMap" $ operationModel+        , testProperty "valid" $ validProperty+        , testProperty "Hashable agree" $ hashableProperty+        , testProperty "aeson roundtrip" $ aesonRoundtrip+        , testProperty "show . read = id" showReadRoundtrip+        ]+    , testGroup "Regressions"+        [ testProperty "issue 10: union overflow" $ issue10+        , testProperty "issue 12 Foldable" $ issue12a+        , testProperty "issue 12 Traversable" $ issue12b+        , testProperty "issue 12 FoldableWithIndex ^.." $ issue12c+        , testProperty "issue 12 FoldableWithIndex ^@.." $ issue12d+        ]     ]  toListFromList :: [(Int, Int)] -> Property@@ -138,3 +149,40 @@     iom = evalOpInsOrd op     rhs = Just iom     lhs = readMaybe $ show iom++-------------------------------------------------------------------------------+-- Regressions+-------------------------------------------------------------------------------++issue12a :: Property+issue12a = (m ^.. folded) === "wold"+  where+    m :: InsOrd.InsOrdHashMap Char Char+    m = InsOrd.fromList  (zip "hello" "world")++issue12b :: Property+issue12b = foldMapDefault (:[]) m === "wold"+  where+    m :: InsOrd.InsOrdHashMap Char Char+    m = InsOrd.fromList  (zip "hello" "world")++issue12c :: Property+issue12c = (m ^.. ifolded) === "wold"+  where+    m :: InsOrd.InsOrdHashMap Char Char+    m = InsOrd.fromList  (zip "hello" "world")++issue12d :: Property+issue12d = (m ^@.. ifolded) === (zip "helo" "wold")+  where+    m :: InsOrd.InsOrdHashMap Char Char+    m = InsOrd.fromList  (zip "hello" "world")+++issue10 :: Property+issue10 = (p ^.. folded) === "wold!" .&&. property (InsOrd.valid p)+  where+    m, n, p :: InsOrd.InsOrdHashMap Char Char+    m = InsOrd.fromList  (zip "hello" "world")+    n = iterate (\x -> InsOrd.union x x) m !! 64+    p = InsOrd.insert '!' '!' n