From c481228ed539aeeb4b19d11384a14a2831cd4b84 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 19 Dec 2021 18:15:53 +0100 Subject: [PATCH 01/21] Migrate benchmarks to tasty-bench (#338) tasty-bench is more actively maintained and supported. gauge doesn't build with GHC 9.2 yet. --- .github/workflows/haskell-ci.yml | 2 +- benchmarks/Benchmarks.hs | 2 +- cabal.haskell-ci | 3 --- unordered-containers.cabal | 6 ++++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/haskell-ci.yml b/.github/workflows/haskell-ci.yml index 1f24273d..350168c3 100644 --- a/.github/workflows/haskell-ci.yml +++ b/.github/workflows/haskell-ci.yml @@ -121,7 +121,7 @@ jobs: HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV" echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV" - if [ $((HCNUMVER < 90200)) -ne 0 ] ; then echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" ; else echo "ARG_BENCH=--disable-benchmarks" >> "$GITHUB_ENV" ; fi + echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV" echo "HEADHACKAGE=false" >> "$GITHUB_ENV" echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV" echo "GHCJSARITH=0" >> "$GITHUB_ENV" diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs index 031aa643..605c1825 100644 --- a/benchmarks/Benchmarks.hs +++ b/benchmarks/Benchmarks.hs @@ -3,7 +3,6 @@ module Main where import Control.DeepSeq -import Gauge (bench, bgroup, defaultMain, env, nf, whnf) import Data.Bits ((.&.)) import Data.Functor.Identity import Data.Hashable (Hashable, hash) @@ -16,6 +15,7 @@ import Data.List (foldl') import Data.Maybe (fromMaybe) import GHC.Generics (Generic) import Prelude hiding (lookup) +import Test.Tasty.Bench (bench, bgroup, defaultMain, env, nf, whnf) import qualified Util.ByteString as UBS import qualified Util.Int as UI diff --git a/cabal.haskell-ci b/cabal.haskell-ci index 04ec5f5e..8ce9b222 100644 --- a/cabal.haskell-ci +++ b/cabal.haskell-ci @@ -2,9 +2,6 @@ branches: master --- Due to https://github.com/haskell-foundation/foundation/issues/554 via gauge. -benchmarks: <9.2 - constraint-set debug constraints: unordered-containers +debug tests: True diff --git a/unordered-containers.cabal b/unordered-containers.cabal index d413e15e..b52052c4 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -202,16 +202,18 @@ benchmark benchmarks base >= 4.8.0, bytestring >= 0.10.0.0, containers, - gauge >= 0.2.5 && < 0.3, deepseq >= 1.4, hashable >= 1.0.1.1, hashmap, mtl, random, + tasty-bench >= 0.3.1, unordered-containers default-language: Haskell2010 - ghc-options: -Wall -O2 -rtsopts -fwarn-tabs -ferror-spans + ghc-options: -Wall -O2 -rtsopts -with-rtsopts=-A32m + if impl(ghc >= 8.10) + ghc-options: "-with-rtsopts=-A32m --nonmoving-gc" source-repository head type: git From b94182326368a665a3075d50b45724c303bf44e0 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 21 Dec 2021 15:54:58 +0100 Subject: [PATCH 02/21] Move comparison benchmarks for containers and hashmap behind CPP (#341) Note that configuring cpp-options in the cabal.project is currently unsupported: https://github.com/haskell/cabal/issues/7872 Closes #333. --- benchmarks/Benchmarks.hs | 33 +++++++++++++++++++++++---------- unordered-containers.cabal | 1 + 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs index 605c1825..486fda58 100644 --- a/benchmarks/Benchmarks.hs +++ b/benchmarks/Benchmarks.hs @@ -121,6 +121,7 @@ main :: IO () main = do defaultMain [ +#ifdef BENCH_containers_Map env setupEnv $ \ ~(Env{..}) -> -- * Comparison to other data structures -- ** Map @@ -161,10 +162,12 @@ main = do [ bench "String" $ whnf (M.isSubmapOf mSubset) m , bench "ByteString" $ whnf (M.isSubmapOf mbsSubset) mbs ] - ] + ], +#endif +#ifdef BENCH_hashmap_Map -- ** Map from the hashmap package - , env setupEnv $ \ ~(Env{..}) -> + env setupEnv $ \ ~(Env{..}) -> bgroup "hashmap/Map" [ bgroup "lookup" [ bench "String" $ whnf (lookupIHM keys) ihm @@ -202,14 +205,12 @@ main = do [ bench "String" $ whnf (IHM.isSubmapOf ihmSubset) ihm , bench "ByteString" $ whnf (IHM.isSubmapOf ihmbsSubset) ihmbs ] - , bgroup "hash" - [ bench "String" $ whnf hash hm - , bench "ByteString" $ whnf hash hmbs - ] - ] + ], +#endif +#ifdef BENCH_containers_IntMap -- ** IntMap - , env setupEnv $ \ ~(Env{..}) -> + env setupEnv $ \ ~(Env{..}) -> bgroup "IntMap" [ bench "lookup" $ whnf (lookupIM keysI) im , bench "lookup-miss" $ whnf (lookupIM keysI') im @@ -220,9 +221,10 @@ main = do , bench "size" $ whnf IM.size im , bench "fromList" $ whnf IM.fromList elemsI , bench "isSubmapOf" $ whnf (IM.isSubmapOf imSubset) im - ] + ], +#endif - , env setupEnv $ \ ~(Env{..}) -> + env setupEnv $ \ ~(Env{..}) -> bgroup "HashMap" [ -- * Basic interface bgroup "lookup" @@ -357,6 +359,11 @@ main = do , bench "Int" $ whnf (HM.fromListWith (+)) elemsDupI ] ] + -- Hashable instance + , bgroup "hash" + [ bench "String" $ whnf hash hm + , bench "ByteString" $ whnf hash hmbs + ] ] ] @@ -438,6 +445,7 @@ isSubmapOfNaive m1 m2 = and [ Just v1 == HM.lookup k1 m2 | (k1,v1) <- HM.toList {-# SPECIALIZE isSubmapOfNaive :: HM.HashMap String Int -> HM.HashMap String Int -> Bool #-} {-# SPECIALIZE isSubmapOfNaive :: HM.HashMap BS.ByteString Int -> HM.HashMap BS.ByteString Int -> Bool #-} +#ifdef BENCH_containers_Map ------------------------------------------------------------------------ -- * Map @@ -458,7 +466,9 @@ deleteM xs m0 = foldl' (\m k -> M.delete k m) m0 xs {-# SPECIALIZE deleteM :: [String] -> M.Map String Int -> M.Map String Int #-} {-# SPECIALIZE deleteM :: [BS.ByteString] -> M.Map BS.ByteString Int -> M.Map BS.ByteString Int #-} +#endif +#ifdef BENCH_hashmap_Map ------------------------------------------------------------------------ -- * Map from the hashmap package @@ -482,7 +492,9 @@ deleteIHM xs m0 = foldl' (\m k -> IHM.delete k m) m0 xs -> IHM.Map String Int #-} {-# SPECIALIZE deleteIHM :: [BS.ByteString] -> IHM.Map BS.ByteString Int -> IHM.Map BS.ByteString Int #-} +#endif +#ifdef BENCH_containers_IntMap ------------------------------------------------------------------------ -- * IntMap @@ -494,3 +506,4 @@ insertIM xs m0 = foldl' (\m (k, v) -> IM.insert k v m) m0 xs deleteIM :: [Int] -> IM.IntMap Int -> IM.IntMap Int deleteIM xs m0 = foldl' (\m k -> IM.delete k m) m0 xs +#endif diff --git a/unordered-containers.cabal b/unordered-containers.cabal index b52052c4..8bff7c96 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -214,6 +214,7 @@ benchmark benchmarks ghc-options: -Wall -O2 -rtsopts -with-rtsopts=-A32m if impl(ghc >= 8.10) ghc-options: "-with-rtsopts=-A32m --nonmoving-gc" + -- cpp-options: -DBENCH_containers_Map -DBENCH_containers_IntMap -DBENCH_hashmap_Map source-repository head type: git From e0c4181ed98e00d4439f098a805830e5135cefbb Mon Sep 17 00:00:00 2001 From: David Feuer Date: Wed, 22 Dec 2021 17:44:20 -0500 Subject: [PATCH 03/21] Remove Typeable deriving crud We no longer need to derive `Typeable` explicitly, so let's not. --- Data/HashMap/Internal.hs | 6 ++---- Data/HashSet/Internal.hs | 7 +++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 7af93c0e..25cb44a3 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE BangPatterns, CPP, DeriveDataTypeable, MagicHash #-} +{-# LANGUAGE BangPatterns, CPP, MagicHash #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RoleAnnotations #-} @@ -143,7 +143,7 @@ import Data.Semigroup (Semigroup((<>))) import Control.DeepSeq (NFData(rnf)) import Control.Monad.ST (ST, runST) import Data.Bits ((.&.), (.|.), complement, popCount, unsafeShiftL, unsafeShiftR) -import Data.Data hiding (Typeable) +import Data.Data import qualified Data.Foldable as Foldable #if MIN_VERSION_base(4,10,0) import Data.Bifoldable @@ -157,7 +157,6 @@ import qualified Data.HashMap.Internal.Array as A import qualified Data.Hashable as H import Data.Hashable (Hashable) import Data.HashMap.Internal.List (isPermutationBy, unorderedCompare) -import Data.Typeable (Typeable) import GHC.Exts (isTrue#) import qualified GHC.Exts as Exts @@ -215,7 +214,6 @@ data HashMap k v | Leaf !Hash !(Leaf k v) | Full !(A.Array (HashMap k v)) | Collision !Hash !(A.Array (Leaf k v)) - deriving (Typeable) type role HashMap nominal representational diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index c5037bc7..0d349a81 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, DeriveDataTypeable #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Trustworthy #-} @@ -89,7 +89,7 @@ module Data.HashSet.Internal ) where import Control.DeepSeq (NFData(..)) -import Data.Data hiding (Typeable) +import Data.Data import Data.Functor.Classes import Data.HashMap.Internal ( HashMap, foldMapWithKey, foldlWithKey, foldrWithKey @@ -104,7 +104,6 @@ import Prelude hiding (filter, foldr, foldl, map, null) import qualified Data.Foldable as Foldable import qualified Data.HashMap.Internal as H import qualified Data.List as List -import Data.Typeable (Typeable) import Text.Read #if MIN_VERSION_hashable(1,2,5) @@ -118,7 +117,7 @@ import qualified Control.DeepSeq as NF -- | A set of values. A set cannot contain duplicate values. newtype HashSet a = HashSet { asMap :: HashMap a () - } deriving (Typeable) + } type role HashSet nominal From bd165b028d6e0e5bec7c5b386734424f94f12caa Mon Sep 17 00:00:00 2001 From: David Feuer Date: Wed, 22 Dec 2021 17:55:46 -0500 Subject: [PATCH 04/21] Add dataCast1 definition for HashMap --- CHANGES.md | 4 ++++ Data/HashMap/Internal.hs | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index b12ec3b1..32375997 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## [0.2.17.0] + +* Define `dataCast1` for `HashMap`. + ## [0.2.16.0] * [Increase maximum branching factor from 16 to 32](https://github.com/haskell-unordered-containers/unordered-containers/pull/317) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 25cb44a3..c65c368e 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -303,6 +303,7 @@ instance (Data k, Data v, Eq k, Hashable k) => Data (HashMap k v) where 1 -> k (z fromList) _ -> error "gunfold" dataTypeOf _ = hashMapDataType + dataCast1 f = gcast1 f dataCast2 f = gcast2 f fromListConstr :: Constr From 6910660d7c3738a1ccb90dec3695fe7ecb64d719 Mon Sep 17 00:00:00 2001 From: David Feuer Date: Sun, 26 Dec 2021 12:13:36 -0500 Subject: [PATCH 05/21] Add Lift instances (#343) Add Lift instances --- CHANGES.md | 2 ++ Data/HashMap/Internal.hs | 23 ++++++++++++++++++++--- Data/HashMap/Internal/Array.hs | 25 +++++++++++++++++++++++++ Data/HashSet/Internal.hs | 6 ++++++ unordered-containers.cabal | 3 ++- 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 32375997..8635179c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ * Define `dataCast1` for `HashMap`. +* [Add `Lift` instances for Template Haskell](https://github.com/haskell-unordered-containers/unordered-containers/pull/343) + ## [0.2.16.0] * [Increase maximum branching factor from 16 to 32](https://github.com/haskell-unordered-containers/unordered-containers/pull/317) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index c65c368e..9af96dd1 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -1,10 +1,15 @@ -{-# LANGUAGE BangPatterns, CPP, MagicHash #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DeriveLift #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MagicHash #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnboxedTuples #-} -{-# LANGUAGE LambdaCase #-} #if __GLASGOW_HASKELL__ >= 802 {-# LANGUAGE TypeInType #-} {-# LANGUAGE UnboxedSums #-} @@ -179,6 +184,7 @@ import GHC.Exts (TYPE, Int (..), Int#) import Data.Functor.Identity (Identity (..)) import Control.Applicative (Const (..)) import Data.Coerce (coerce) +import qualified Language.Haskell.TH.Syntax as TH -- | A set of values. A set cannot contain duplicate values. ------------------------------------------------------------------------ @@ -193,6 +199,14 @@ data Leaf k v = L !k v instance (NFData k, NFData v) => NFData (Leaf k v) where rnf (L k v) = rnf k `seq` rnf v +-- | @since 0.2.17.0 +instance (TH.Lift k, TH.Lift v) => TH.Lift (Leaf k v) where +#if MIN_VERSION_template_haskell(2,16,0) + liftTyped (L k v) = [|| L k $! v ||] +#else + lift (L k v) = [| L k $! v |] +#endif + #if MIN_VERSION_deepseq(1,4,3) -- | @since 0.2.14.0 instance NFData k => NF.NFData1 (Leaf k) where @@ -217,6 +231,9 @@ data HashMap k v type role HashMap nominal representational +-- | @since 0.2.17.0 +deriving instance (TH.Lift k, TH.Lift v) => TH.Lift (HashMap k v) + instance (NFData k, NFData v) => NFData (HashMap k v) where rnf Empty = () rnf (BitmapIndexed _ ary) = rnf ary diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index 0ed6b088..4ddffb11 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -1,4 +1,5 @@ {-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples, ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskellQuotes #-} {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} {-# OPTIONS_HADDOCK not-home #-} @@ -69,6 +70,7 @@ module Data.HashMap.Internal.Array , traverse' , toList , fromList + , fromList' ) where import Control.Applicative (liftA2) @@ -84,6 +86,8 @@ import GHC.Exts (SmallArray#, newSmallArray#, readSmallArray#, writeSmallArray#, SmallMutableArray#, sizeofSmallArray#, copySmallArray#, thawSmallArray#, sizeofSmallMutableArray#, copySmallMutableArray#, cloneSmallMutableArray#) +import qualified Language.Haskell.TH.Syntax as TH + #if defined(ASSERTS) import qualified Prelude #endif @@ -474,6 +478,27 @@ fromList n xs0 = go (x:xs) mary i = do write mary i x go xs mary (i+1) +fromList' :: Int -> [a] -> Array a +fromList' n xs0 = + CHECK_EQ("fromList'", n, Prelude.length xs0) + run $ do + mary <- new_ n + go xs0 mary 0 + where + go [] !mary !_ = return mary + go (!x:xs) mary i = do write mary i x + go xs mary (i+1) + +instance TH.Lift a => TH.Lift (Array a) where +#if MIN_VERSION_template_haskell(2,16,0) + liftTyped ar = [|| fromList' arlen arlist ||] +#else + lift ar = [| fromList' arlen arlist |] +#endif + where + arlen = length ar + arlist = toList ar + toList :: Array a -> [a] toList = foldr (:) [] diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index 0d349a81..1071fc7e 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveLift #-} {-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Trustworthy #-} {-# OPTIONS_HADDOCK not-home #-} @@ -113,6 +115,7 @@ import qualified Data.Hashable.Lifted as H #if MIN_VERSION_deepseq(1,4,3) import qualified Control.DeepSeq as NF #endif +import qualified Language.Haskell.TH.Syntax as TH -- | A set of values. A set cannot contain duplicate values. newtype HashSet a = HashSet { @@ -121,6 +124,9 @@ newtype HashSet a = HashSet { type role HashSet nominal +-- | @since 0.2.17.0 +deriving instance TH.Lift a => TH.Lift (HashSet a) + instance (NFData a) => NFData (HashSet a) where rnf = rnf . asMap {-# INLINE rnf #-} diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 8bff7c96..85d102e0 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -56,7 +56,8 @@ library build-depends: base >= 4.9 && < 5, deepseq >= 1.1, - hashable >= 1.0.1.1 && < 1.5 + hashable >= 1.0.1.1 && < 1.5, + template-haskell < 2.19 default-language: Haskell2010 From 65470386697f6a5b5f4ffd09137fda146f2b1871 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 6 Jan 2022 13:49:49 +0100 Subject: [PATCH 06/21] Add definitions for `stimes` (#340) Also remove unused `LambdaCase` extension. Resolves part of #307. --- Data/HashMap/Internal.hs | 6 +++--- Data/HashMap/Internal/Strict.hs | 1 - Data/HashSet/Internal.hs | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 9af96dd1..cf60bb43 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -142,9 +142,7 @@ module Data.HashMap.Internal , adjust# ) where -#if !MIN_VERSION_base(4,11,0) -import Data.Semigroup (Semigroup((<>))) -#endif +import Data.Semigroup (Semigroup(..), stimesIdempotentMonoid) import Control.DeepSeq (NFData(rnf)) import Control.Monad.ST (ST, runST) import Data.Bits ((.&.), (.|.), complement, popCount, unsafeShiftL, unsafeShiftR) @@ -296,6 +294,8 @@ instance Bifoldable HashMap where instance (Eq k, Hashable k) => Semigroup (HashMap k v) where (<>) = union {-# INLINE (<>) #-} + stimes = stimesIdempotentMonoid + {-# INLINE stimes #-} -- | 'mempty' = 'empty' -- diff --git a/Data/HashMap/Internal/Strict.hs b/Data/HashMap/Internal/Strict.hs index 725452fc..ef74a30f 100644 --- a/Data/HashMap/Internal/Strict.hs +++ b/Data/HashMap/Internal/Strict.hs @@ -1,5 +1,4 @@ {-# LANGUAGE BangPatterns, CPP, PatternGuards, MagicHash, UnboxedTuples #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE Trustworthy #-} {-# OPTIONS_HADDOCK not-home #-} diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index 1071fc7e..e5e483db 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -97,9 +97,7 @@ import Data.HashMap.Internal ( HashMap, foldMapWithKey, foldlWithKey, foldrWithKey , equalKeys, equalKeys1) import Data.Hashable (Hashable(hashWithSalt)) -#if !MIN_VERSION_base(4,11,0) -import Data.Semigroup (Semigroup(..)) -#endif +import Data.Semigroup (Semigroup(..), stimesIdempotentMonoid) import GHC.Exts (build) import qualified GHC.Exts as Exts import Prelude hiding (filter, foldr, foldl, map, null) @@ -200,6 +198,8 @@ instance Foldable.Foldable HashSet where instance (Hashable a, Eq a) => Semigroup (HashSet a) where (<>) = union {-# INLINE (<>) #-} + stimes = stimesIdempotentMonoid + {-# INLINE stimes #-} -- | 'mempty' = 'empty' -- From ecbc722e59d5ec118a1f9baaac7b7f787a2fc838 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 12 Jan 2022 00:14:05 +0100 Subject: [PATCH 07/21] Expose internal constructors (#347) Fixes #342. --- Data/HashMap/Internal/Array.hs | 4 ++-- Data/HashSet/Internal.hs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index 4ddffb11..aac9cc75 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -21,8 +21,8 @@ -- -- Note that no bounds checking are performed. module Data.HashMap.Internal.Array - ( Array - , MArray + ( Array(..) + , MArray(..) -- * Creation , new diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index e5e483db..e0ed5fe3 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -42,7 +42,7 @@ module Data.HashSet.Internal ( - HashSet + HashSet(..) -- * Construction , empty From d84a308b79fd29230156115fd2673e0a3ac2e021 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 13 Jan 2022 20:24:10 +0100 Subject: [PATCH 08/21] developer-guide: Update B (#348) B was changed in https://github.com/haskell-unordered-containers/unordered-containers/pull/317. --- docs/developer-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index b9fc8437..07bba306 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -103,7 +103,7 @@ Here's a quick overview in order of simplicty: it contains *2^B* elements. The number of bits of the hash value to use at each level of the tree, *B*, is a -compiled time constant (i.e. 4). In general a larger *B* improves lookup +compile time constant, currently 5. In general a larger *B* improves lookup performance (shallower tree) but hurts modification (large nodes to copy when updating the spine of the tree). From 7d93030ab0dc943303b1fce7cc250e141f31491c Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sat, 22 Jan 2022 00:26:08 +0100 Subject: [PATCH 09/21] u-c.cabal: Update maintainers (#339) This change reflects who has been doing active maintenance work in the past 2 years. --- unordered-containers.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 85d102e0..39752e45 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -18,7 +18,7 @@ description: license: BSD3 license-file: LICENSE author: Johan Tibell -maintainer: johan.tibell@gmail.com, David.Feuer@gmail.com +maintainer: simon.jakobi@gmail.com, David.Feuer@gmail.com Homepage: https://github.com/haskell-unordered-containers/unordered-containers bug-reports: https://github.com/haskell-unordered-containers/unordered-containers/issues copyright: 2010-2014 Johan Tibell From b7a79aa121b9ccf71997b280e9f8df424a0d7123 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Feb 2022 12:59:59 +0100 Subject: [PATCH 10/21] Migrate tests from test-framework to tasty (#316) --- tests/HashMapProperties.hs | 10 +++++----- tests/HashSetProperties.hs | 10 +++++----- tests/List.hs | 8 ++++---- tests/Regressions.hs | 10 +++++----- tests/Strictness.hs | 8 ++++---- unordered-containers.cabal | 26 +++++++++++++------------- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/HashMapProperties.hs b/tests/HashMapProperties.hs index cbf4b25a..480eb327 100644 --- a/tests/HashMapProperties.hs +++ b/tests/HashMapProperties.hs @@ -2,7 +2,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} -- because of Arbitrary (HashMap k v) -- | Tests for the 'Data.HashMap.Lazy' module. We test functions by --- comparing them to a simpler model, an association list. +-- comparing them to @Map@ from @containers@. module Main (main) where @@ -25,8 +25,8 @@ import qualified Data.HashMap.Lazy as HM import qualified Data.Map.Lazy as M #endif import Test.QuickCheck (Arbitrary(..), Property, (==>), (===), forAll, elements) -import Test.Framework (Test, defaultMain, testGroup) -import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.QuickCheck (testProperty) import Data.Functor.Identity (Identity (..)) import Control.Applicative (Const (..)) import Test.QuickCheck.Function (Fun, apply) @@ -440,8 +440,8 @@ pKeys = (L.sort . M.keys) `eq` (L.sort . HM.keys) ------------------------------------------------------------------------ -- * Test list -tests :: [Test] -tests = +tests :: TestTree +tests = testGroup "HashMap properties" [ -- Instances testGroup "instances" diff --git a/tests/HashSetProperties.hs b/tests/HashSetProperties.hs index 9aa80bb2..1891d957 100644 --- a/tests/HashSetProperties.hs +++ b/tests/HashSetProperties.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-} -- | Tests for the 'Data.HashSet' module. We test functions by --- comparing them to a simpler model, a list. +-- comparing them to @Set@ from @containers@. module Main (main) where @@ -12,8 +12,8 @@ import qualified Data.HashSet as S import qualified Data.Set as Set import Data.Ord (comparing) import Test.QuickCheck (Arbitrary, Property, (==>), (===)) -import Test.Framework (Test, defaultMain, testGroup) -import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.QuickCheck (testProperty) -- Key type that generates more hash collisions. newtype Key = K { unK :: Int } @@ -159,8 +159,8 @@ pToList = Set.toAscList `eq` toAscList ------------------------------------------------------------------------ -- * Test list -tests :: [Test] -tests = +tests :: TestTree +tests = testGroup "HashSet properties" [ -- Instances testGroup "instances" diff --git a/tests/List.hs b/tests/List.hs index f95889df..8df8e6a3 100644 --- a/tests/List.hs +++ b/tests/List.hs @@ -4,11 +4,11 @@ import Data.HashMap.Internal.List import Data.List (nub, sort, sortBy) import Data.Ord (comparing) -import Test.Framework (Test, defaultMain, testGroup) -import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck ((==>), (===), property, Property) -tests :: Test +tests :: TestTree tests = testGroup "Data.HashMap.Internal.List" [ testProperty "isPermutationBy" pIsPermutation , testProperty "isPermutationBy of different length" pIsPermutationDiffLength @@ -65,4 +65,4 @@ pUnorderedCompare xs ys = unorderedCompare compare xs ys === modelUnorderedCompare xs ys main :: IO () -main = defaultMain [tests] +main = defaultMain tests diff --git a/tests/Regressions.hs b/tests/Regressions.hs index fbd16db0..70a54015 100644 --- a/tests/Regressions.hs +++ b/tests/Regressions.hs @@ -16,9 +16,9 @@ import System.Mem (performGC) import System.Mem.Weak (mkWeakPtr, deRefWeak) import System.Random (randomIO) import Test.HUnit (Assertion, assert) -import Test.Framework (Test, defaultMain) -import Test.Framework.Providers.HUnit (testCase) -import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.HUnit (testCase) +import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck issue32 :: Assertion @@ -126,8 +126,8 @@ issue254Strict = do ------------------------------------------------------------------------ -- * Test list -tests :: [Test] -tests = +tests :: TestTree +tests = testGroup "Regression tests" [ testCase "issue32" issue32 , testCase "issue39a" issue39 diff --git a/tests/Strictness.hs b/tests/Strictness.hs index a0c36dda..ee54233c 100644 --- a/tests/Strictness.hs +++ b/tests/Strictness.hs @@ -5,8 +5,8 @@ module Main (main) where import Data.Hashable (Hashable(hashWithSalt)) import Test.ChasingBottoms.IsBottom -import Test.Framework (Test, defaultMain, testGroup) -import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck (Arbitrary(arbitrary), Property, (===), (.&&.)) import Test.QuickCheck.Function import Test.QuickCheck.Poly (A) @@ -149,8 +149,8 @@ pFromListWithValueResultStrict lst comb_lazy calc_good_raw ------------------------------------------------------------------------ -- * Test list -tests :: [Test] -tests = +tests :: TestTree +tests = testGroup "Strictness tests" [ -- Basic interface testGroup "HashMap.Strict" diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 39752e45..9b021a33 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -88,8 +88,8 @@ test-suite hashmap-lazy-properties containers >= 0.5.8, hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1, - test-framework >= 0.3.3, - test-framework-quickcheck2 >= 0.2.9, + tasty >= 1.4.0.3, + tasty-quickcheck >= 0.10.1.2, unordered-containers default-language: Haskell2010 @@ -106,8 +106,8 @@ test-suite hashmap-strict-properties containers >= 0.5.8, hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1, - test-framework >= 0.3.3, - test-framework-quickcheck2 >= 0.2.9, + tasty >= 1.4.0.3, + tasty-quickcheck >= 0.10.1.2, unordered-containers default-language: Haskell2010 @@ -124,8 +124,8 @@ test-suite hashset-properties containers >= 0.4.2.0, hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1, - test-framework >= 0.3.3, - test-framework-quickcheck2 >= 0.2.9, + tasty >= 1.4.0.3, + tasty-quickcheck >= 0.10.1.2, unordered-containers default-language: Haskell2010 @@ -143,8 +143,8 @@ test-suite list-tests base, containers >= 0.4, QuickCheck >= 2.4.0.1, - test-framework >= 0.3.3, - test-framework-quickcheck2 >= 0.2.9 + tasty >= 1.4.0.3, + tasty-quickcheck >= 0.10.1.2 default-language: Haskell2010 ghc-options: -Wall @@ -161,9 +161,9 @@ test-suite regressions HUnit, QuickCheck >= 2.4.0.1, random, - test-framework >= 0.3.3, - test-framework-hunit, - test-framework-quickcheck2, + tasty >= 1.4.0.3, + tasty-hunit >= 0.10.0.3, + tasty-quickcheck >= 0.10.1.2, unordered-containers default-language: Haskell2010 @@ -181,8 +181,8 @@ test-suite strictness-properties containers >= 0.4.2, hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1, - test-framework >= 0.3.3, - test-framework-quickcheck2 >= 0.2.9, + tasty >= 1.4.0.3, + tasty-quickcheck >= 0.10.1.2, unordered-containers default-language: Haskell2010 From c48237994d8a2476c5029e494feb052a7354e29a Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 18 Feb 2022 21:27:47 +0100 Subject: [PATCH 11/21] Merge testsuites into one (#351) This should improve dev ergonomics and speed up building and running the tests. Context: #284 --- tests/Main.hs | 14 +++ tests/Properties.hs | 16 +++ .../HashMapLazy.hs} | 22 ++-- tests/Properties/HashMapStrict.hs | 5 + .../HashSet.hs} | 12 +- tests/{ => Properties}/List.hs | 7 +- tests/Regressions.hs | 10 +- tests/Strictness.hs | 12 +- unordered-containers.cabal | 103 ++---------------- 9 files changed, 69 insertions(+), 132 deletions(-) create mode 100644 tests/Main.hs create mode 100644 tests/Properties.hs rename tests/{HashMapProperties.hs => Properties/HashMapLazy.hs} (98%) create mode 100644 tests/Properties/HashMapStrict.hs rename tests/{HashSetProperties.hs => Properties/HashSet.hs} (96%) rename tests/{ => Properties}/List.hs (94%) diff --git a/tests/Main.hs b/tests/Main.hs new file mode 100644 index 00000000..c18ae77d --- /dev/null +++ b/tests/Main.hs @@ -0,0 +1,14 @@ +module Main (main) where + +import Test.Tasty (defaultMain, testGroup) + +import qualified Regressions +import qualified Properties +import qualified Strictness + +main :: IO () +main = defaultMain $ testGroup "All" + [ Properties.tests + , Regressions.tests + , Strictness.tests + ] diff --git a/tests/Properties.hs b/tests/Properties.hs new file mode 100644 index 00000000..01acc420 --- /dev/null +++ b/tests/Properties.hs @@ -0,0 +1,16 @@ +module Properties (tests) where + +import Test.Tasty (TestTree, testGroup) + +import qualified Properties.HashMapLazy +import qualified Properties.HashMapStrict +import qualified Properties.HashSet +import qualified Properties.List + +tests :: TestTree +tests = testGroup "Properties" + [ Properties.HashMapLazy.tests + , Properties.HashMapStrict.tests + , Properties.HashSet.tests + , Properties.List.tests + ] diff --git a/tests/HashMapProperties.hs b/tests/Properties/HashMapLazy.hs similarity index 98% rename from tests/HashMapProperties.hs rename to tests/Properties/HashMapLazy.hs index 480eb327..e1d582bd 100644 --- a/tests/HashMapProperties.hs +++ b/tests/Properties/HashMapLazy.hs @@ -4,7 +4,11 @@ -- | Tests for the 'Data.HashMap.Lazy' module. We test functions by -- comparing them to @Map@ from @containers@. -module Main (main) where +#if defined(STRICT) +module Properties.HashMapStrict (tests) where +#else +module Properties.HashMapLazy (tests) where +#endif import Control.Monad ( guard ) import qualified Data.Foldable as Foldable @@ -25,7 +29,7 @@ import qualified Data.HashMap.Lazy as HM import qualified Data.Map.Lazy as M #endif import Test.QuickCheck (Arbitrary(..), Property, (==>), (===), forAll, elements) -import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) import Data.Functor.Identity (Identity (..)) import Control.Applicative (Const (..)) @@ -441,7 +445,13 @@ pKeys = (L.sort . M.keys) `eq` (L.sort . HM.keys) -- * Test list tests :: TestTree -tests = testGroup "HashMap properties" +tests = + testGroup +#if defined(STRICT) + "Data.HashMap.Strict" +#else + "Data.HashMap.Lazy" +#endif [ -- Instances testGroup "instances" @@ -571,12 +581,6 @@ eq_ f g = (M.toAscList . f) `eq` (toAscList . g) infix 4 `eq_` ------------------------------------------------------------------------- --- * Test harness - -main :: IO () -main = defaultMain tests - ------------------------------------------------------------------------ -- * Helpers diff --git a/tests/Properties/HashMapStrict.hs b/tests/Properties/HashMapStrict.hs new file mode 100644 index 00000000..238348df --- /dev/null +++ b/tests/Properties/HashMapStrict.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE CPP #-} + +#define STRICT + +#include "HashMapLazy.hs" diff --git a/tests/HashSetProperties.hs b/tests/Properties/HashSet.hs similarity index 96% rename from tests/HashSetProperties.hs rename to tests/Properties/HashSet.hs index 1891d957..5564057b 100644 --- a/tests/HashSetProperties.hs +++ b/tests/Properties/HashSet.hs @@ -3,7 +3,7 @@ -- | Tests for the 'Data.HashSet' module. We test functions by -- comparing them to @Set@ from @containers@. -module Main (main) where +module Properties.HashSet (tests) where import qualified Data.Foldable as Foldable import Data.Hashable (Hashable(hashWithSalt)) @@ -12,7 +12,7 @@ import qualified Data.HashSet as S import qualified Data.Set as Set import Data.Ord (comparing) import Test.QuickCheck (Arbitrary, Property, (==>), (===)) -import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) -- Key type that generates more hash collisions. @@ -160,7 +160,7 @@ pToList = Set.toAscList `eq` toAscList -- * Test list tests :: TestTree -tests = testGroup "HashSet properties" +tests = testGroup "Data.HashSet" [ -- Instances testGroup "instances" @@ -227,12 +227,6 @@ eq_ :: (Eq a, Hashable a, Ord a) -- equivalent eq_ f g = (Set.toAscList . f) `eq` (toAscList . g) ------------------------------------------------------------------------- --- * Test harness - -main :: IO () -main = defaultMain tests - ------------------------------------------------------------------------ -- * Helpers diff --git a/tests/List.hs b/tests/Properties/List.hs similarity index 94% rename from tests/List.hs rename to tests/Properties/List.hs index 8df8e6a3..1e3f87ba 100644 --- a/tests/List.hs +++ b/tests/Properties/List.hs @@ -1,10 +1,10 @@ -module Main (main) where +module Properties.List (tests) where import Data.HashMap.Internal.List import Data.List (nub, sort, sortBy) import Data.Ord (comparing) -import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck ((==>), (===), property, Property) @@ -63,6 +63,3 @@ modelUnorderedCompareTrans xs ys zs = pUnorderedCompare :: [Int] -> [Int] -> Property pUnorderedCompare xs ys = unorderedCompare compare xs ys === modelUnorderedCompare xs ys - -main :: IO () -main = defaultMain tests diff --git a/tests/Regressions.hs b/tests/Regressions.hs index 70a54015..51d72ad9 100644 --- a/tests/Regressions.hs +++ b/tests/Regressions.hs @@ -1,7 +1,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE UnboxedTuples #-} -module Main where +module Regressions (tests) where import Control.Exception (evaluate) import Control.Monad (replicateM) @@ -16,7 +16,7 @@ import System.Mem (performGC) import System.Mem.Weak (mkWeakPtr, deRefWeak) import System.Random (randomIO) import Test.HUnit (Assertion, assert) -import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase) import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck @@ -135,9 +135,3 @@ tests = testGroup "Regression tests" , testCase "issue254 lazy" issue254Lazy , testCase "issue254 strict" issue254Strict ] - ------------------------------------------------------------------------- --- * Test harness - -main :: IO () -main = defaultMain tests diff --git a/tests/Strictness.hs b/tests/Strictness.hs index ee54233c..f80e1bb6 100644 --- a/tests/Strictness.hs +++ b/tests/Strictness.hs @@ -1,11 +1,11 @@ {-# LANGUAGE CPP, FlexibleInstances, GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Main (main) where +module Strictness (tests) where import Data.Hashable (Hashable(hashWithSalt)) import Test.ChasingBottoms.IsBottom -import Test.Tasty (TestTree, defaultMain, testGroup) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck (Arbitrary(arbitrary), Property, (===), (.&&.)) import Test.QuickCheck.Function @@ -150,7 +150,7 @@ pFromListWithValueResultStrict lst comb_lazy calc_good_raw -- * Test list tests :: TestTree -tests = testGroup "Strictness tests" +tests = testGroup "Strictness" [ -- Basic interface testGroup "HashMap.Strict" @@ -175,12 +175,6 @@ tests = testGroup "Strictness tests" ] ] ------------------------------------------------------------------------- --- * Test harness - -main :: IO () -main = defaultMain tests - ------------------------------------------------------------------------ -- * Utilities diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 9b021a33..1dc086ac 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -78,85 +78,23 @@ library if flag(debug) cpp-options: -DASSERTS -test-suite hashmap-lazy-properties +test-suite unordered-containers-tests hs-source-dirs: tests - main-is: HashMapProperties.hs + main-is: Main.hs type: exitcode-stdio-1.0 - - build-depends: - base, - containers >= 0.5.8, - hashable >= 1.0.1.1, - QuickCheck >= 2.4.0.1, - tasty >= 1.4.0.3, - tasty-quickcheck >= 0.10.1.2, - unordered-containers - - default-language: Haskell2010 - ghc-options: -Wall - cpp-options: -DASSERTS - -test-suite hashmap-strict-properties - hs-source-dirs: tests - main-is: HashMapProperties.hs - type: exitcode-stdio-1.0 - - build-depends: - base, - containers >= 0.5.8, - hashable >= 1.0.1.1, - QuickCheck >= 2.4.0.1, - tasty >= 1.4.0.3, - tasty-quickcheck >= 0.10.1.2, - unordered-containers - - default-language: Haskell2010 - ghc-options: -Wall - cpp-options: -DASSERTS -DSTRICT - -test-suite hashset-properties - hs-source-dirs: tests - main-is: HashSetProperties.hs - type: exitcode-stdio-1.0 - - build-depends: - base, - containers >= 0.4.2.0, - hashable >= 1.0.1.1, - QuickCheck >= 2.4.0.1, - tasty >= 1.4.0.3, - tasty-quickcheck >= 0.10.1.2, - unordered-containers - - default-language: Haskell2010 - ghc-options: -Wall - cpp-options: -DASSERTS - -test-suite list-tests - hs-source-dirs: tests . - main-is: List.hs other-modules: - Data.HashMap.Internal.List - type: exitcode-stdio-1.0 - - build-depends: - base, - containers >= 0.4, - QuickCheck >= 2.4.0.1, - tasty >= 1.4.0.3, - tasty-quickcheck >= 0.10.1.2 - - default-language: Haskell2010 - ghc-options: -Wall - cpp-options: -DASSERTS - -test-suite regressions - hs-source-dirs: tests - main-is: Regressions.hs - type: exitcode-stdio-1.0 + Regressions + Properties + Properties.HashMapLazy + Properties.HashMapStrict + Properties.HashSet + Properties.List + Strictness build-depends: base, + ChasingBottoms, + containers >= 0.5.8, hashable >= 1.0.1.1, HUnit, QuickCheck >= 2.4.0.1, @@ -170,25 +108,6 @@ test-suite regressions ghc-options: -Wall cpp-options: -DASSERTS -test-suite strictness-properties - hs-source-dirs: tests - main-is: Strictness.hs - type: exitcode-stdio-1.0 - - build-depends: - base, - ChasingBottoms, - containers >= 0.4.2, - hashable >= 1.0.1.1, - QuickCheck >= 2.4.0.1, - tasty >= 1.4.0.3, - tasty-quickcheck >= 0.10.1.2, - unordered-containers - - default-language: Haskell2010 - ghc-options: -Wall - cpp-options: -DASSERTS - benchmark benchmarks hs-source-dirs: benchmarks main-is: Benchmarks.hs From 58c455e7bba5406c210e9aff5a218b91535de923 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 1 Mar 2022 15:56:11 +0100 Subject: [PATCH 12/21] Add documentation on building, testing, benchmarking (#352) --- CONTRIBUTING.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +++-- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..fd91a78e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +# Contributing + +## Building, testing, benchmarking + +Building the library: + +``` +cabal build +``` + +Running the testsuite: + +``` +cabal test +``` + +Viewing the test options: + +``` +cabal run tests -- --help +``` + +Running a specific property test with an increased number of test cases +(default: 100 cases): + +``` +cabal run tests -- -p '/All.Properties.Data.HashSet.basic interface.member/' --quickcheck-tests 100_000 +``` + +Running the benchmarks: + +``` +cabal bench +``` + +Viewing the benchmark options: + +``` +cabal run benches -- --help +``` + +Running a specific benchmark with a reduced target standard deviation (default: +5%): + +``` +cabal run benches -- -p /All.HashMap.lookup-miss.ByteString/ --stdev 1 +``` + +To include comparison benchmarks for `containers` and `hashmap` uncomment the +`cpp-options` in the benchmark section of `unordered-containers.cabal`: + +``` +cpp-options: -DBENCH_containers_Map -DBENCH_containers_IntMap -DBENCH_hashmap_Map +``` + +### References + +* [Documentation for `cabal`](https://cabal.readthedocs.io/en/latest/) +* [Documentation for our testing framework, `tasty`](https://github.com/UnkindPartition/tasty#readme) +* [Documentation for our benchmark framework, `tasty-bench`](https://github.com/Bodigrim/tasty-bench#readme) diff --git a/README.md b/README.md index ee0403b3..5caec826 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ performance critical use, both in terms of large data quantities and high speed. The declared cost of each operation is either worst-case or amortized, but remains valid even if structures are shared. - -For developer and contributor documentation see the + +For background information and design considerations on this package see the [Developer Guide](docs/developer-guide.md). + +For practical advice for contributors see [`CONTRIBUTING.md`](CONTRIBUTING.md). From a94f518fe75418ff3377a5fa58c6623c2c6e6589 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 1 Mar 2022 22:07:09 +0100 Subject: [PATCH 13/21] Changelog updates --- CHANGES.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 8635179c..ca5e7f1b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,15 @@ ## [0.2.17.0] -* Define `dataCast1` for `HashMap`. +* [Define `dataCast1` for `HashMap`](https://github.com/haskell-unordered-containers/unordered-containers/pull/345) * [Add `Lift` instances for Template Haskell](https://github.com/haskell-unordered-containers/unordered-containers/pull/343) +* [Add definitions for `stimes`](https://github.com/haskell-unordered-containers/unordered-containers/pull/340) + +* [Expose internal constructors for `HashSet`, `Array` and `MArray`](https://github.com/haskell-unordered-containers/unordered-containers/pull/347) + +[0.2.17.0]: https://github.com/haskell-unordered-containers/unordered-containers/compare/v0.2.16.0...v0.2.17.0 + ## [0.2.16.0] * [Increase maximum branching factor from 16 to 32](https://github.com/haskell-unordered-containers/unordered-containers/pull/317) From 8628140a27cb33400acb18851152757f1a3d5777 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Mar 2022 19:25:35 +0100 Subject: [PATCH 14/21] Drop support for GHC 8.0 (#354) This allows us to remove a good amount of CPP and hacks. --- .github/workflows/haskell-ci.yml | 19 ++++++--------- Data/HashMap/Internal.hs | 41 -------------------------------- Data/HashMap/Internal/Array.hs | 4 ---- Data/HashSet/Internal.hs | 4 ---- tests/Properties/HashMapLazy.hs | 6 ----- unordered-containers.cabal | 12 +++------- utils/Stats.hs | 3 --- 7 files changed, 10 insertions(+), 79 deletions(-) diff --git a/.github/workflows/haskell-ci.yml b/.github/workflows/haskell-ci.yml index 350168c3..1bc148d6 100644 --- a/.github/workflows/haskell-ci.yml +++ b/.github/workflows/haskell-ci.yml @@ -8,9 +8,9 @@ # # For more information, see https://github.com/haskell-CI/haskell-ci # -# version: 0.13.20211116 +# version: 0.14.1 # -# REGENDATA ("0.13.20211116",["github","unordered-containers.cabal"]) +# REGENDATA ("0.14.1",["github","unordered-containers.cabal"]) # name: Haskell-CI on: @@ -37,10 +37,10 @@ jobs: compilerVersion: 9.2.1 setup-method: ghcup allow-failure: false - - compiler: ghc-9.0.1 + - compiler: ghc-9.0.2 compilerKind: ghc - compilerVersion: 9.0.1 - setup-method: hvr-ppa + compilerVersion: 9.0.2 + setup-method: ghcup allow-failure: false - compiler: ghc-8.10.7 compilerKind: ghc @@ -67,11 +67,6 @@ jobs: compilerVersion: 8.2.2 setup-method: hvr-ppa allow-failure: false - - compiler: ghc-8.0.2 - compilerKind: ghc - compilerVersion: 8.0.2 - setup-method: hvr-ppa - allow-failure: false fail-fast: false steps: - name: apt @@ -198,8 +193,8 @@ jobs: touch cabal.project touch cabal.project.local echo "packages: ${PKGDIR_unordered_containers}" >> cabal.project - if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package unordered-containers" >> cabal.project ; fi - if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods" >> cabal.project ; fi + echo "package unordered-containers" >> cabal.project + echo " ghc-options: -Werror=missing-methods" >> cabal.project cat >> cabal.project <> cabal.project.local diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index cf60bb43..65d4cf07 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -10,10 +10,8 @@ {-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnboxedTuples #-} -#if __GLASGOW_HASKELL__ >= 802 {-# LANGUAGE TypeInType #-} {-# LANGUAGE UnboxedSums #-} -#endif {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} {-# OPTIONS_HADDOCK not-home #-} @@ -148,9 +146,7 @@ import Control.Monad.ST (ST, runST) import Data.Bits ((.&.), (.|.), complement, popCount, unsafeShiftL, unsafeShiftR) import Data.Data import qualified Data.Foldable as Foldable -#if MIN_VERSION_base(4,10,0) import Data.Bifoldable -#endif import qualified Data.List as L import GHC.Exts ((==#), build, reallyUnsafePtrEquality#, inline) import Prelude hiding (filter, foldl, foldr, lookup, map, null, pred) @@ -171,13 +167,9 @@ import GHC.Stack import qualified Data.Hashable.Lifted as H #endif -#if MIN_VERSION_deepseq(1,4,3) import qualified Control.DeepSeq as NF -#endif -#if __GLASGOW_HASKELL__ >= 802 import GHC.Exts (TYPE, Int (..), Int#) -#endif import Data.Functor.Identity (Identity (..)) import Control.Applicative (Const (..)) @@ -205,7 +197,6 @@ instance (TH.Lift k, TH.Lift v) => TH.Lift (Leaf k v) where lift (L k v) = [| L k $! v |] #endif -#if MIN_VERSION_deepseq(1,4,3) -- | @since 0.2.14.0 instance NFData k => NF.NFData1 (Leaf k) where liftRnf rnf2 = NF.liftRnf2 rnf rnf2 @@ -213,7 +204,6 @@ instance NFData k => NF.NFData1 (Leaf k) where -- | @since 0.2.14.0 instance NF.NFData2 Leaf where liftRnf2 rnf1 rnf2 (L k v) = rnf1 k `seq` rnf2 v -#endif -- Invariant: The length of the 1st argument to 'Full' is -- 2^bitsPerSubkey @@ -239,7 +229,6 @@ instance (NFData k, NFData v) => NFData (HashMap k v) where rnf (Full ary) = rnf ary rnf (Collision _ ary) = rnf ary -#if MIN_VERSION_deepseq(1,4,3) -- | @since 0.2.14.0 instance NFData k => NF.NFData1 (HashMap k) where liftRnf rnf2 = NF.liftRnf2 rnf rnf2 @@ -251,7 +240,6 @@ instance NF.NFData2 HashMap where liftRnf2 rnf1 rnf2 (Leaf _ l) = NF.liftRnf2 rnf1 rnf2 l liftRnf2 rnf1 rnf2 (Full ary) = NF.liftRnf (NF.liftRnf2 rnf1 rnf2) ary liftRnf2 rnf1 rnf2 (Collision _ ary) = NF.liftRnf (NF.liftRnf2 rnf1 rnf2) ary -#endif instance Functor (HashMap k) where fmap = map @@ -272,7 +260,6 @@ instance Foldable.Foldable (HashMap k) where length = size {-# INLINE length #-} -#if MIN_VERSION_base(4,10,0) -- | @since 0.2.11 instance Bifoldable HashMap where bifoldMap f g = foldMapWithKey (\ k v -> f k `mappend` g v) @@ -281,7 +268,6 @@ instance Bifoldable HashMap where {-# INLINE bifoldr #-} bifoldl f g = foldlWithKey (\ acc k v -> (acc `f` k) `g` v) {-# INLINE bifoldl #-} -#endif -- | '<>' = 'union' -- @@ -606,7 +592,6 @@ member k m = case lookup k m of -- | /O(log n)/ Return the value to which the specified key is mapped, -- or 'Nothing' if this map contains no mapping for the key. lookup :: (Eq k, Hashable k) => k -> HashMap k v -> Maybe v -#if __GLASGOW_HASKELL__ >= 802 -- GHC does not yet perform a worker-wrapper transformation on -- unboxed sums automatically. That seems likely to happen at some -- point (possibly as early as GHC 8.6) but for now we do it manually. @@ -619,16 +604,9 @@ lookup# :: (Eq k, Hashable k) => k -> HashMap k v -> (# (# #) | v #) lookup# k m = lookupCont (\_ -> (# (# #) | #)) (\v _i -> (# | v #)) (hash k) k 0 m {-# INLINABLE lookup# #-} -#else - -lookup k m = lookupCont (\_ -> Nothing) (\v _i -> Just v) (hash k) k 0 m -{-# INLINABLE lookup #-} -#endif - -- | lookup' is a version of lookup that takes the hash separately. -- It is used to implement alterF. lookup' :: Eq k => Hash -> k -> HashMap k v -> Maybe v -#if __GLASGOW_HASKELL__ >= 802 -- GHC does not yet perform a worker-wrapper transformation on -- unboxed sums automatically. That seems likely to happen at some -- point (possibly as early as GHC 8.6) but for now we do it manually. @@ -639,10 +617,6 @@ lookup' h k m = case lookupRecordCollision# h k m of (# (# #) | #) -> Nothing (# | (# a, _i #) #) -> Just a {-# INLINE lookup' #-} -#else -lookup' h k m = lookupCont (\_ -> Nothing) (\v _i -> Just v) h k 0 m -{-# INLINABLE lookup' #-} -#endif -- The result of a lookup, keeping track of if a hash collision occured. -- If a collision did not occur then it will have the Int value (-1). @@ -662,7 +636,6 @@ data LookupRes a = Absent | Present a !Int -- Key in map, no collision => Present v (-1) -- Key in map, collision => Present v position lookupRecordCollision :: Eq k => Hash -> k -> HashMap k v -> LookupRes v -#if __GLASGOW_HASKELL__ >= 802 lookupRecordCollision h k m = case lookupRecordCollision# h k m of (# (# #) | #) -> Absent (# | (# a, i #) #) -> Present a (I# i) -- GHC will eliminate the I# @@ -679,12 +652,6 @@ lookupRecordCollision# h k m = -- INLINABLE to specialize to the Eq instance. {-# INLINABLE lookupRecordCollision# #-} -#else /* GHC < 8.2 so there are no unboxed sums */ - -lookupRecordCollision h k m = lookupCont (\_ -> Absent) Present h k 0 m -{-# INLINABLE lookupRecordCollision #-} -#endif - -- A two-continuation version of lookupRecordCollision. This lets us -- share source code between lookup and lookupRecordCollision without -- risking any performance degradation. @@ -698,11 +665,7 @@ lookupRecordCollision h k m = lookupCont (\_ -> Absent) Present h k 0 m -- keys at the top-level of a hashmap, the offset should be 0. When looking up -- keys at level @n@ of a hashmap, the offset should be @n * bitsPerSubkey@. lookupCont :: -#if __GLASGOW_HASKELL__ >= 802 forall rep (r :: TYPE rep) k v. -#else - forall r k v. -#endif Eq k => ((# #) -> r) -- Absent continuation -> (v -> Int -> r) -- Present continuation @@ -2155,11 +2118,7 @@ fromListWithKey f = L.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty -- | /O(n)/ Look up the value associated with the given key in an -- array. lookupInArrayCont :: -#if __GLASGOW_HASKELL__ >= 802 forall rep (r :: TYPE rep) k v. -#else - forall r k v. -#endif Eq k => ((# #) -> r) -> (v -> Int -> r) -> k -> A.Array (Leaf k v) -> r lookupInArrayCont absent present k0 ary0 = go k0 ary0 0 (A.length ary0) where diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index aac9cc75..a2215764 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -92,9 +92,7 @@ import qualified Language.Haskell.TH.Syntax as TH import qualified Prelude #endif -#if MIN_VERSION_deepseq(1,4,3) import qualified Control.DeepSeq as NF -#endif import Control.Monad ((>=>)) @@ -173,7 +171,6 @@ rnfArray ary0 = go ary0 n0 0 -- relevant rnf is strict, or in case it actually isn't. {-# INLINE rnfArray #-} -#if MIN_VERSION_deepseq(1,4,3) -- | @since 0.2.14.0 instance NF.NFData1 Array where liftRnf = liftRnfArray @@ -187,7 +184,6 @@ liftRnfArray rnf0 ary0 = go ary0 n0 0 | (# x #) <- index# ary i = rnf0 x `seq` go ary n (i+1) {-# INLINE liftRnfArray #-} -#endif -- | Create a new mutable array of specified size, in the specified -- state thread, with each element containing the specified initial diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index e0ed5fe3..83478f0b 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -110,9 +110,7 @@ import Text.Read import qualified Data.Hashable.Lifted as H #endif -#if MIN_VERSION_deepseq(1,4,3) import qualified Control.DeepSeq as NF -#endif import qualified Language.Haskell.TH.Syntax as TH -- | A set of values. A set cannot contain duplicate values. @@ -129,11 +127,9 @@ instance (NFData a) => NFData (HashSet a) where rnf = rnf . asMap {-# INLINE rnf #-} -#if MIN_VERSION_deepseq(1,4,3) -- | @since 0.2.14.0 instance NF.NFData1 HashSet where liftRnf rnf1 = NF.liftRnf2 rnf1 rnf . asMap -#endif -- | Note that, in the presence of hash collisions, equal @HashSet@s may -- behave differently, i.e. substitutivity may be violated: diff --git a/tests/Properties/HashMapLazy.hs b/tests/Properties/HashMapLazy.hs index e1d582bd..b783c4f1 100644 --- a/tests/Properties/HashMapLazy.hs +++ b/tests/Properties/HashMapLazy.hs @@ -12,9 +12,7 @@ module Properties.HashMapLazy (tests) where import Control.Monad ( guard ) import qualified Data.Foldable as Foldable -#if MIN_VERSION_base(4,10,0) import Data.Bifoldable -#endif import Data.Function (on) import Data.Hashable (Hashable(hashWithSalt)) import qualified Data.List as L @@ -337,7 +335,6 @@ pFoldr = (L.sort . M.foldr (:) []) `eq` (L.sort . HM.foldr (:) []) pFoldl :: [(Int, Int)] -> Bool pFoldl = (L.sort . M.foldl (flip (:)) []) `eq` (L.sort . HM.foldl (flip (:)) []) -#if MIN_VERSION_base(4,10,0) pBifoldMap :: [(Int, Int)] -> Bool pBifoldMap xs = concatMap f (HM.toList m) == bifoldMap (:[]) (:[]) m where f (k, v) = [k, v] @@ -352,7 +349,6 @@ pBifoldl :: [(Int, Int)] -> Bool pBifoldl xs = reverse (concatMap f $ HM.toList m) == bifoldl (flip (:)) (flip (:)) [] m where f (k, v) = [k, v] m = HM.fromList xs -#endif pFoldrWithKey :: [(Int, Int)] -> Bool pFoldrWithKey = (sortByKey . M.foldrWithKey f []) `eq` @@ -514,11 +510,9 @@ tests = , testGroup "folds" [ testProperty "foldr" pFoldr , testProperty "foldl" pFoldl -#if MIN_VERSION_base(4,10,0) , testProperty "bifoldMap" pBifoldMap , testProperty "bifoldr" pBifoldr , testProperty "bifoldl" pBifoldl -#endif , testProperty "foldrWithKey" pFoldrWithKey , testProperty "foldlWithKey" pFoldlWithKey , testProperty "foldrWithKey'" pFoldrWithKey' diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 1dc086ac..7bf037aa 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -30,13 +30,12 @@ extra-source-files: CHANGES.md tested-with: GHC ==9.2.1 - || ==9.0.1 + || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 - || ==8.0.2 flag debug description: Enable debug support @@ -54,8 +53,8 @@ library Data.HashSet.Internal build-depends: - base >= 4.9 && < 5, - deepseq >= 1.1, + base >= 4.10 && < 5, + deepseq >= 1.4.3, hashable >= 1.0.1.1 && < 1.5, template-haskell < 2.19 @@ -70,11 +69,6 @@ library ghc-options: -Wall -O2 -fwarn-tabs -ferror-spans - if impl (ghc < 8.2) - -- This is absolutely necessary (but not sufficient) for correctness due to - -- the referential-transparency-breaking mutability in unsafeInsertWith. See - -- #147 and GHC #13615 for details. The bug was fixed in GHC 8.2. - ghc-options: -feager-blackholing if flag(debug) cpp-options: -DASSERTS diff --git a/utils/Stats.hs b/utils/Stats.hs index 8b01ecdc..c0150c82 100644 --- a/utils/Stats.hs +++ b/utils/Stats.hs @@ -27,9 +27,6 @@ instance Semigroup Histogram where instance Monoid Histogram where mempty = H 0 0 0 0 0 -#if __GLASGOW_HASKELL__ < 803 - mappend = (<>) -#endif -- | Count the number of node types at each level nodeHistogram :: HM.HashMap k v -> [Histogram] From 150022867210d956a684b7f8bd58410b59e264da Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Mar 2022 19:27:17 +0100 Subject: [PATCH 15/21] Tweak Array.deleteM --- Data/HashMap/Internal/Array.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index a2215764..ad3bb435 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -423,13 +423,13 @@ delete ary idx = runST (deleteM ary idx) -- | /O(n)/ Delete an element at the given position in this array, -- decreasing its size by one. deleteM :: Array e -> Int -> ST s (Array e) -deleteM ary idx = do +deleteM ary0 idx = do CHECK_BOUNDS("deleteM", count, idx) - do mary <- new_ (count-1) - copy ary 0 mary 0 idx - copy ary (idx+1) mary idx (count-(idx+1)) - unsafeFreeze mary - where !count = length ary + do mary0 <- unsafeThaw ary0 + mary1 <- cloneM mary0 0 (count-1) + copy ary0 (idx+1) mary1 idx (count-(idx+1)) + unsafeFreeze mary1 + where !count = length ary0 {-# INLINE deleteM #-} map :: (a -> b) -> Array a -> Array b From 7d4ca70c849a82d9d233574396b3bdf71bbd7648 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Mar 2022 19:28:03 +0100 Subject: [PATCH 16/21] Revert "Tweak Array.deleteM" This reverts commit 150022867210d956a684b7f8bd58410b59e264da. (Accidental push to master) --- Data/HashMap/Internal/Array.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index ad3bb435..a2215764 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -423,13 +423,13 @@ delete ary idx = runST (deleteM ary idx) -- | /O(n)/ Delete an element at the given position in this array, -- decreasing its size by one. deleteM :: Array e -> Int -> ST s (Array e) -deleteM ary0 idx = do +deleteM ary idx = do CHECK_BOUNDS("deleteM", count, idx) - do mary0 <- unsafeThaw ary0 - mary1 <- cloneM mary0 0 (count-1) - copy ary0 (idx+1) mary1 idx (count-(idx+1)) - unsafeFreeze mary1 - where !count = length ary0 + do mary <- new_ (count-1) + copy ary 0 mary 0 idx + copy ary (idx+1) mary idx (count-(idx+1)) + unsafeFreeze mary + where !count = length ary {-# INLINE deleteM #-} map :: (a -> b) -> Array a -> Array b From 5c57564bdc044cbd36719cb75d6b8c849ad3fd92 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Mar 2022 22:02:17 +0100 Subject: [PATCH 17/21] Array.insertM: Remove write operation (#359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …by initializing the array with the inserted element. --- Data/HashMap/Internal/Array.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index a2215764..00010bc8 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -303,9 +303,8 @@ insert ary idx b = runST (insertM ary idx b) insertM :: Array e -> Int -> e -> ST s (Array e) insertM ary idx b = CHECK_BOUNDS("insertM", count + 1, idx) - do mary <- new_ (count+1) + do mary <- new (count+1) b copy ary 0 mary 0 idx - write mary idx b copy ary idx mary (idx+1) (count-idx) unsafeFreeze mary where !count = length ary From f59bb2601204ac37abcc460eb04c7227340a8805 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Mar 2022 22:03:08 +0100 Subject: [PATCH 18/21] Drop support for hashable < 1.2.5 (#355) Stackage snapshots for GHC 8.2 use hashable-1.2.7.0, so this shouldn't cause any problems. --- Data/HashMap/Internal.hs | 4 ---- Data/HashSet/Internal.hs | 4 ---- unordered-containers.cabal | 8 ++++---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 65d4cf07..557842ec 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -163,9 +163,7 @@ import qualified GHC.Exts as Exts import Data.Functor.Classes import GHC.Stack -#if MIN_VERSION_hashable(1,2,5) import qualified Data.Hashable.Lifted as H -#endif import qualified Control.DeepSeq as NF @@ -486,7 +484,6 @@ equalKeys = go leafEq (L k1 _) (L k2 _) = k1 == k2 -#if MIN_VERSION_hashable(1,2,5) instance H.Hashable2 HashMap where liftHashWithSalt2 hk hv salt hm = go salt (toList' hm []) where @@ -512,7 +509,6 @@ instance H.Hashable2 HashMap where instance (Hashable k) => H.Hashable1 (HashMap k) where liftHashWithSalt = H.liftHashWithSalt2 H.hashWithSalt -#endif instance (Hashable k, Hashable v) => Hashable (HashMap k v) where hashWithSalt salt hm = go salt hm diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index 83478f0b..2676d6ce 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -106,9 +106,7 @@ import qualified Data.HashMap.Internal as H import qualified Data.List as List import Text.Read -#if MIN_VERSION_hashable(1,2,5) import qualified Data.Hashable.Lifted as H -#endif import qualified Control.DeepSeq as NF import qualified Language.Haskell.TH.Syntax as TH @@ -241,10 +239,8 @@ instance (Data a, Eq a, Hashable a) => Data (HashSet a) where dataTypeOf _ = hashSetDataType dataCast1 f = gcast1 f -#if MIN_VERSION_hashable(1,2,6) instance H.Hashable1 HashSet where liftHashWithSalt h s = H.liftHashWithSalt2 h hashWithSalt s . asMap -#endif instance (Hashable a) => Hashable (HashSet a) where hashWithSalt salt = hashWithSalt salt . asMap diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 7bf037aa..755649a0 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -55,7 +55,7 @@ library build-depends: base >= 4.10 && < 5, deepseq >= 1.4.3, - hashable >= 1.0.1.1 && < 1.5, + hashable >= 1.2.5 && < 1.5, template-haskell < 2.19 default-language: Haskell2010 @@ -89,7 +89,7 @@ test-suite unordered-containers-tests base, ChasingBottoms, containers >= 0.5.8, - hashable >= 1.0.1.1, + hashable, HUnit, QuickCheck >= 2.4.0.1, random, @@ -116,8 +116,8 @@ benchmark benchmarks base >= 4.8.0, bytestring >= 0.10.0.0, containers, - deepseq >= 1.4, - hashable >= 1.0.1.1, + deepseq, + hashable, hashmap, mtl, random, From 96c58c4787803a8f1cbbe1872f026dab080e2943 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 4 Mar 2022 03:30:34 +0100 Subject: [PATCH 19/21] Fix reference to equal2 (#365) `equal` was renamed to `equal2` in #193. --- Data/HashMap/Internal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 557842ec..0f3a3c86 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -452,7 +452,7 @@ cmp cmpk cmpv t1 t2 = go (toList' t1 []) (toList' t2 []) leafCompare (L k v) (L k' v') = cmpk k k' `mappend` cmpv v v' --- Same as 'equal' but doesn't compare the values. +-- Same as 'equal2' but doesn't compare the values. equalKeys1 :: (k -> k' -> Bool) -> HashMap k v -> HashMap k' v' -> Bool equalKeys1 eq t1 t2 = go (toList' t1 []) (toList' t2 []) where From 42a25dbc19babf7c1153ae19bdef609f8308de04 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 4 Mar 2022 17:10:54 +0100 Subject: [PATCH 20/21] Style imports and pragmas with stylish-haskell (#356) Also: * Tweak some module abbreviations * Properties.HashMapLazy: Tweak CPP for stylish-haskell * CONTRIBUTING.md: Add code style section --- .stylish-haskell.yaml | 9 ++ CONTRIBUTING.md | 16 ++++ Data/HashMap/Internal.hs | 153 ++++++++++++++++---------------- Data/HashMap/Internal/Array.hs | 39 ++++---- Data/HashMap/Internal/List.hs | 10 ++- Data/HashMap/Internal/Strict.hs | 47 +++++----- Data/HashMap/Lazy.hs | 7 +- Data/HashMap/Strict.hs | 5 +- Data/HashSet.hs | 4 +- Data/HashSet/Internal.hs | 57 ++++++------ benchmarks/Benchmarks.hs | 41 +++++---- benchmarks/Util/ByteString.hs | 5 +- tests/Main.hs | 2 +- tests/Properties/HashMapLazy.hs | 78 ++++++++-------- tests/Properties/HashSet.hs | 44 ++++----- tests/Properties/List.hs | 11 ++- tests/Regressions.hs | 55 ++++++------ tests/Strictness.hs | 25 +++--- utils/Stats.hs | 9 +- 19 files changed, 332 insertions(+), 285 deletions(-) create mode 100644 .stylish-haskell.yaml diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml new file mode 100644 index 00000000..3131af78 --- /dev/null +++ b/.stylish-haskell.yaml @@ -0,0 +1,9 @@ +steps: + - imports: + align: group + pad_module_names: true + long_list_align: inline + - language_pragmas: + align: true + remove_redundant: true + language_prefix: LANGUAGE diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd91a78e..007230bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,3 +58,19 @@ cpp-options: -DBENCH_containers_Map -DBENCH_containers_IntMap -DBENCH_hashmap_Ma * [Documentation for `cabal`](https://cabal.readthedocs.io/en/latest/) * [Documentation for our testing framework, `tasty`](https://github.com/UnkindPartition/tasty#readme) * [Documentation for our benchmark framework, `tasty-bench`](https://github.com/Bodigrim/tasty-bench#readme) + + +## Code style + +This package uses [`stylish-haskell`](https://hackage.haskell.org/package/stylish-haskell) +to format language pragmas and import sections. To format a specific file, run + +``` +stylish-haskell -i FILENAME +``` + +To format all the Haskell files under a specific directory, run + +``` +stylish-haskell -ir DIRNAME +``` diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs index 0f3a3c86..826af55f 100644 --- a/Data/HashMap/Internal.hs +++ b/Data/HashMap/Internal.hs @@ -1,17 +1,17 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE DeriveLift #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE RoleAnnotations #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveLift #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskellQuotes #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UnboxedTuples #-} -{-# LANGUAGE TypeInType #-} -{-# LANGUAGE UnboxedSums #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeInType #-} +{-# LANGUAGE UnboxedSums #-} +{-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} {-# OPTIONS_HADDOCK not-home #-} @@ -140,39 +140,36 @@ module Data.HashMap.Internal , adjust# ) where -import Data.Semigroup (Semigroup(..), stimesIdempotentMonoid) -import Control.DeepSeq (NFData(rnf)) -import Control.Monad.ST (ST, runST) -import Data.Bits ((.&.), (.|.), complement, popCount, unsafeShiftL, unsafeShiftR) -import Data.Data -import qualified Data.Foldable as Foldable -import Data.Bifoldable -import qualified Data.List as L -import GHC.Exts ((==#), build, reallyUnsafePtrEquality#, inline) -import Prelude hiding (filter, foldl, foldr, lookup, map, null, pred) -import Text.Read hiding (step) - -import qualified Data.HashMap.Internal.Array as A -import qualified Data.Hashable as H -import Data.Hashable (Hashable) +import Control.Applicative (Const (..)) +import Control.DeepSeq (NFData (..), NFData1 (..), NFData2 (..)) +import Control.Monad.ST (ST, runST) +import Data.Bifoldable (Bifoldable (..)) +import Data.Bits (complement, popCount, unsafeShiftL, + unsafeShiftR, (.&.), (.|.)) +import Data.Coerce (coerce) +import Data.Data (Constr, Data (..), DataType) +import Data.Functor.Classes (Eq1 (..), Eq2 (..), Ord1 (..), Ord2 (..), + Read1 (..), Show1 (..), Show2 (..)) +import Data.Functor.Identity (Identity (..)) import Data.HashMap.Internal.List (isPermutationBy, unorderedCompare) - -import GHC.Exts (isTrue#) -import qualified GHC.Exts as Exts - -import Data.Functor.Classes -import GHC.Stack - -import qualified Data.Hashable.Lifted as H - -import qualified Control.DeepSeq as NF - -import GHC.Exts (TYPE, Int (..), Int#) - -import Data.Functor.Identity (Identity (..)) -import Control.Applicative (Const (..)) -import Data.Coerce (coerce) -import qualified Language.Haskell.TH.Syntax as TH +import Data.Hashable (Hashable) +import Data.Hashable.Lifted (Hashable1, Hashable2) +import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid) +import GHC.Exts (Int (..), Int#, TYPE, (==#)) +import GHC.Stack (HasCallStack) +import Prelude hiding (filter, foldl, foldr, lookup, map, + null, pred) +import Text.Read hiding (step) + +import qualified Data.Data as Data +import qualified Data.Foldable as Foldable +import qualified Data.Functor.Classes as FC +import qualified Data.HashMap.Internal.Array as A +import qualified Data.Hashable as H +import qualified Data.Hashable.Lifted as H +import qualified Data.List as List +import qualified GHC.Exts as Exts +import qualified Language.Haskell.TH.Syntax as TH -- | A set of values. A set cannot contain duplicate values. ------------------------------------------------------------------------ @@ -196,11 +193,11 @@ instance (TH.Lift k, TH.Lift v) => TH.Lift (Leaf k v) where #endif -- | @since 0.2.14.0 -instance NFData k => NF.NFData1 (Leaf k) where - liftRnf rnf2 = NF.liftRnf2 rnf rnf2 +instance NFData k => NFData1 (Leaf k) where + liftRnf rnf2 = liftRnf2 rnf rnf2 -- | @since 0.2.14.0 -instance NF.NFData2 Leaf where +instance NFData2 Leaf where liftRnf2 rnf1 rnf2 (L k v) = rnf1 k `seq` rnf2 v -- Invariant: The length of the 1st argument to 'Full' is @@ -228,16 +225,16 @@ instance (NFData k, NFData v) => NFData (HashMap k v) where rnf (Collision _ ary) = rnf ary -- | @since 0.2.14.0 -instance NFData k => NF.NFData1 (HashMap k) where - liftRnf rnf2 = NF.liftRnf2 rnf rnf2 +instance NFData k => NFData1 (HashMap k) where + liftRnf rnf2 = liftRnf2 rnf rnf2 -- | @since 0.2.14.0 -instance NF.NFData2 HashMap where +instance NFData2 HashMap where liftRnf2 _ _ Empty = () - liftRnf2 rnf1 rnf2 (BitmapIndexed _ ary) = NF.liftRnf (NF.liftRnf2 rnf1 rnf2) ary - liftRnf2 rnf1 rnf2 (Leaf _ l) = NF.liftRnf2 rnf1 rnf2 l - liftRnf2 rnf1 rnf2 (Full ary) = NF.liftRnf (NF.liftRnf2 rnf1 rnf2) ary - liftRnf2 rnf1 rnf2 (Collision _ ary) = NF.liftRnf (NF.liftRnf2 rnf1 rnf2) ary + liftRnf2 rnf1 rnf2 (BitmapIndexed _ ary) = liftRnf (liftRnf2 rnf1 rnf2) ary + liftRnf2 rnf1 rnf2 (Leaf _ l) = liftRnf2 rnf1 rnf2 l + liftRnf2 rnf1 rnf2 (Full ary) = liftRnf (liftRnf2 rnf1 rnf2) ary + liftRnf2 rnf1 rnf2 (Collision _ ary) = liftRnf (liftRnf2 rnf1 rnf2) ary instance Functor (HashMap k) where fmap = map @@ -300,18 +297,18 @@ instance (Eq k, Hashable k) => Monoid (HashMap k v) where instance (Data k, Data v, Eq k, Hashable k) => Data (HashMap k v) where gfoldl f z m = z fromList `f` toList m toConstr _ = fromListConstr - gunfold k z c = case constrIndex c of + gunfold k z c = case Data.constrIndex c of 1 -> k (z fromList) _ -> error "gunfold" dataTypeOf _ = hashMapDataType - dataCast1 f = gcast1 f - dataCast2 f = gcast2 f + dataCast1 f = Data.gcast1 f + dataCast2 f = Data.gcast2 f fromListConstr :: Constr -fromListConstr = mkConstr hashMapDataType "fromList" [] Prefix +fromListConstr = Data.mkConstr hashMapDataType "fromList" [] Data.Prefix hashMapDataType :: DataType -hashMapDataType = mkDataType "Data.HashMap.Internal.HashMap" [fromListConstr] +hashMapDataType = Data.mkDataType "Data.HashMap.Internal.HashMap" [fromListConstr] type Hash = Word type Bitmap = Word @@ -319,7 +316,7 @@ type Shift = Int instance Show2 HashMap where liftShowsPrec2 spk slk spv slv d m = - showsUnaryWith (liftShowsPrec sp sl) "fromList" d (toList m) + FC.showsUnaryWith (liftShowsPrec sp sl) "fromList" d (toList m) where sp = liftShowsPrec2 spk slk spv slv sl = liftShowList2 spk slk spv slv @@ -328,8 +325,8 @@ instance Show k => Show1 (HashMap k) where liftShowsPrec = liftShowsPrec2 showsPrec showList instance (Eq k, Hashable k, Read k) => Read1 (HashMap k) where - liftReadsPrec rp rl = readsData $ - readsUnaryWith (liftReadsPrec rp' rl') "fromList" fromList + liftReadsPrec rp rl = FC.readsData $ + FC.readsUnaryWith (liftReadsPrec rp' rl') "fromList" fromList where rp' = liftReadsPrec rp rl rl' = liftReadList rp rl @@ -484,7 +481,7 @@ equalKeys = go leafEq (L k1 _) (L k2 _) = k1 == k2 -instance H.Hashable2 HashMap where +instance Hashable2 HashMap where liftHashWithSalt2 hk hv salt hm = go salt (toList' hm []) where -- go :: Int -> [HashMap k v] -> Int @@ -502,12 +499,12 @@ instance H.Hashable2 HashMap where -- hashCollisionWithSalt :: Int -> A.Array (Leaf k v) -> Int hashCollisionWithSalt s - = L.foldl' H.hashWithSalt s . arrayHashesSorted s + = List.foldl' H.hashWithSalt s . arrayHashesSorted s -- arrayHashesSorted :: Int -> A.Array (Leaf k v) -> [Int] - arrayHashesSorted s = L.sort . L.map (hashLeafWithSalt s) . A.toList + arrayHashesSorted s = List.sort . List.map (hashLeafWithSalt s) . A.toList -instance (Hashable k) => H.Hashable1 (HashMap k) where +instance (Hashable k) => Hashable1 (HashMap k) where liftHashWithSalt = H.liftHashWithSalt2 H.hashWithSalt instance (Hashable k, Hashable v) => Hashable (HashMap k v) where @@ -529,10 +526,10 @@ instance (Hashable k, Hashable v) => Hashable (HashMap k v) where hashCollisionWithSalt :: Int -> A.Array (Leaf k v) -> Int hashCollisionWithSalt s - = L.foldl' H.hashWithSalt s . arrayHashesSorted s + = List.foldl' H.hashWithSalt s . arrayHashesSorted s arrayHashesSorted :: Int -> A.Array (Leaf k v) -> [Int] - arrayHashesSorted s = L.sort . L.map (hashLeafWithSalt s) . A.toList + arrayHashesSorted s = List.sort . List.map (hashLeafWithSalt s) . A.toList -- Helper to get 'Leaf's and 'Collision's as a list. toList' :: HashMap k v -> [HashMap k v] -> [HashMap k v] @@ -1410,7 +1407,7 @@ alterFEager f !k m = (<$> f mv) $ \fres -> -- -- @since 0.2.12 isSubmapOf :: (Eq k, Hashable k, Eq v) => HashMap k v -> HashMap k v -> Bool -isSubmapOf = (inline isSubmapOfBy) (==) +isSubmapOf = (Exts.inline isSubmapOfBy) (==) {-# INLINABLE isSubmapOf #-} -- | /O(n*log m)/ Inclusion of maps with value comparison. A map is included in @@ -1652,7 +1649,7 @@ unionArrayBy f b1 b2 ary1 ary2 = A.run $ do -- | Construct a set containing all elements from a list of sets. unions :: (Eq k, Hashable k) => [HashMap k v] -> HashMap k v -unions = L.foldl' union empty +unions = List.foldl' union empty {-# INLINE unions #-} @@ -2020,13 +2017,13 @@ filter p = filterWithKey (\_ v -> p v) -- | /O(n)/ Return a list of this map's keys. The list is produced -- lazily. keys :: HashMap k v -> [k] -keys = L.map fst . toList +keys = List.map fst . toList {-# INLINE keys #-} -- | /O(n)/ Return a list of this map's values. The list is produced -- lazily. elems :: HashMap k v -> [v] -elems = L.map snd . toList +elems = List.map snd . toList {-# INLINE elems #-} ------------------------------------------------------------------------ @@ -2035,13 +2032,13 @@ elems = L.map snd . toList -- | /O(n)/ Return a list of this map's elements. The list is -- produced lazily. The order of its elements is unspecified. toList :: HashMap k v -> [(k, v)] -toList t = build (\ c z -> foldrWithKey (curry c) z t) +toList t = Exts.build (\ c z -> foldrWithKey (curry c) z t) {-# INLINE toList #-} -- | /O(n)/ Construct a map with the supplied mappings. If the list -- contains duplicate mappings, the later mappings take precedence. fromList :: (Eq k, Hashable k) => [(k, v)] -> HashMap k v -fromList = L.foldl' (\ m (k, v) -> unsafeInsert k v m) empty +fromList = List.foldl' (\ m (k, v) -> unsafeInsert k v m) empty {-# INLINABLE fromList #-} -- | /O(n*log n)/ Construct a map from a list of elements. Uses @@ -2075,7 +2072,7 @@ fromList = L.foldl' (\ m (k, v) -> unsafeInsert k v m) empty -- > fromListWith f [(k, a), (k, b), (k, c), (k, d)] -- > = fromList [(k, f d (f c (f b a)))] fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v -fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty +fromListWith f = List.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty {-# INLINE fromListWith #-} -- | /O(n*log n)/ Construct a map from a list of elements. Uses @@ -2105,7 +2102,7 @@ fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty -- -- @since 0.2.11 fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v -fromListWithKey f = L.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty +fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty {-# INLINE fromListWithKey #-} ------------------------------------------------------------------------ @@ -2282,7 +2279,7 @@ fullNodeMask = complement (complement 0 `unsafeShiftL` maxChildren) -- | Check if two the two arguments are the same value. N.B. This -- function might give false negatives (due to GC moving objects.) ptrEq :: a -> a -> Bool -ptrEq x y = isTrue# (reallyUnsafePtrEquality# x y ==# 1#) +ptrEq x y = Exts.isTrue# (Exts.reallyUnsafePtrEquality# x y ==# 1#) {-# INLINE ptrEq #-} ------------------------------------------------------------------------ diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index 00010bc8..78059ba9 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -1,5 +1,10 @@ -{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples, ScopedTypeVariables #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskellQuotes #-} +{-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} {-# OPTIONS_HADDOCK not-home #-} @@ -74,27 +79,27 @@ module Data.HashMap.Internal.Array ) where import Control.Applicative (liftA2) -import Control.DeepSeq (NFData (..)) -import GHC.Exts(Int(..), reallyUnsafePtrEquality#, tagToEnum#, unsafeCoerce#) -import GHC.ST (ST(..)) -import Control.Monad.ST (runST, stToIO) - -import Prelude hiding (filter, foldMap, foldr, foldl, length, map, read, traverse, all) - -import GHC.Exts (SmallArray#, newSmallArray#, readSmallArray#, writeSmallArray#, - indexSmallArray#, unsafeFreezeSmallArray#, unsafeThawSmallArray#, - SmallMutableArray#, sizeofSmallArray#, copySmallArray#, thawSmallArray#, - sizeofSmallMutableArray#, copySmallMutableArray#, cloneSmallMutableArray#) +import Control.DeepSeq (NFData (..), NFData1 (..)) +import Control.Monad ((>=>)) +import Control.Monad.ST (runST, stToIO) +import GHC.Exts (Int (..), SmallArray#, SmallMutableArray#, + cloneSmallMutableArray#, copySmallArray#, + copySmallMutableArray#, indexSmallArray#, + newSmallArray#, readSmallArray#, + reallyUnsafePtrEquality#, sizeofSmallArray#, + sizeofSmallMutableArray#, tagToEnum#, + thawSmallArray#, unsafeCoerce#, + unsafeFreezeSmallArray#, unsafeThawSmallArray#, + writeSmallArray#) +import GHC.ST (ST (..)) +import Prelude hiding (all, filter, foldMap, foldl, foldr, length, + map, read, traverse) import qualified Language.Haskell.TH.Syntax as TH - #if defined(ASSERTS) import qualified Prelude #endif -import qualified Control.DeepSeq as NF - -import Control.Monad ((>=>)) #if defined(ASSERTS) -- This fugly hack is brought by GHC's apparent reluctance to deal @@ -172,7 +177,7 @@ rnfArray ary0 = go ary0 n0 0 {-# INLINE rnfArray #-} -- | @since 0.2.14.0 -instance NF.NFData1 Array where +instance NFData1 Array where liftRnf = liftRnfArray liftRnfArray :: (a -> ()) -> Array a -> () diff --git a/Data/HashMap/Internal/List.hs b/Data/HashMap/Internal/List.hs index 8c0b639b..01b1d92c 100644 --- a/Data/HashMap/Internal/List.hs +++ b/Data/HashMap/Internal/List.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} {-# OPTIONS_HADDOCK not-home #-} @@ -25,10 +26,11 @@ module Data.HashMap.Internal.List , unorderedCompare ) where +import Data.List (sortBy) import Data.Maybe (fromMaybe) -import Data.List (sortBy) -import Data.Monoid -import Prelude +#if !MIN_VERSION_base(4,11,0) +import Data.Semigroup ((<>)) +#endif -- Note: previous implemenation isPermutation = null (as // bs) -- was O(n^2) too. @@ -68,7 +70,7 @@ unorderedCompare c as bs = go (sortBy cmpA as) (sortBy cmpB bs) go [] [] = EQ go [] (_ : _) = LT go (_ : _) [] = GT - go (x : xs) (y : ys) = c x y `mappend` go xs ys + go (x : xs) (y : ys) = c x y <> go xs ys cmpA a a' = compare (inB a) (inB a') cmpB b b' = compare (inA b) (inA b') diff --git a/Data/HashMap/Internal/Strict.hs b/Data/HashMap/Internal/Strict.hs index ef74a30f..8f8effcb 100644 --- a/Data/HashMap/Internal/Strict.hs +++ b/Data/HashMap/Internal/Strict.hs @@ -1,5 +1,9 @@ -{-# LANGUAGE BangPatterns, CPP, PatternGuards, MagicHash, UnboxedTuples #-} -{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_HADDOCK not-home #-} ------------------------------------------------------------------------ @@ -117,24 +121,23 @@ module Data.HashMap.Internal.Strict , fromListWithKey ) where -import Control.Monad.ST (runST) -import Data.Bits ((.&.), (.|.)) - -import qualified Data.List as L -import Data.Hashable (Hashable) -import Prelude hiding (map, lookup) - +import Control.Applicative (Const (..)) +import Control.Monad.ST (runST) +import Data.Bits ((.&.), (.|.)) +import Data.Coerce (coerce) +import Data.Functor.Identity (Identity (..)) +import Data.HashMap.Internal hiding (adjust, alter, alterF, differenceWith, + fromList, fromListWith, fromListWithKey, insert, + insertWith, intersectionWith, intersectionWithKey, + map, mapMaybe, mapMaybeWithKey, mapWithKey, + singleton, traverseWithKey, unionWith, + unionWithKey, update) +import Data.Hashable (Hashable) +import Prelude hiding (lookup, map) + +import qualified Data.HashMap.Internal as HM import qualified Data.HashMap.Internal.Array as A -import qualified Data.HashMap.Internal as HM -import Data.HashMap.Internal hiding ( - alter, alterF, adjust, fromList, fromListWith, fromListWithKey, - insert, insertWith, - differenceWith, intersectionWith, intersectionWithKey, map, mapWithKey, - mapMaybe, mapMaybeWithKey, singleton, update, unionWith, unionWithKey, - traverseWithKey) -import Data.Functor.Identity -import Control.Applicative (Const (..)) -import Data.Coerce +import qualified Data.List as List -- $strictness -- @@ -627,7 +630,7 @@ intersectionWithKey f a b = foldlWithKey' go empty a -- list contains duplicate mappings, the later mappings take -- precedence. fromList :: (Eq k, Hashable k) => [(k, v)] -> HashMap k v -fromList = L.foldl' (\ m (k, !v) -> HM.unsafeInsert k v m) empty +fromList = List.foldl' (\ m (k, !v) -> HM.unsafeInsert k v m) empty {-# INLINABLE fromList #-} -- | /O(n*log n)/ Construct a map from a list of elements. Uses @@ -661,7 +664,7 @@ fromList = L.foldl' (\ m (k, !v) -> HM.unsafeInsert k v m) empty -- > fromListWith f [(k, a), (k, b), (k, c), (k, d)] -- > = fromList [(k, f d (f c (f b a)))] fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v -fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty +fromListWith f = List.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty {-# INLINE fromListWith #-} -- | /O(n*log n)/ Construct a map from a list of elements. Uses @@ -691,7 +694,7 @@ fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty -- -- @since 0.2.11 fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v -fromListWithKey f = L.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty +fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty {-# INLINE fromListWithKey #-} ------------------------------------------------------------------------ diff --git a/Data/HashMap/Lazy.hs b/Data/HashMap/Lazy.hs index 27759b67..0b54115c 100644 --- a/Data/HashMap/Lazy.hs +++ b/Data/HashMap/Lazy.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE Trustworthy #-} ------------------------------------------------------------------------ @@ -106,9 +106,10 @@ module Data.HashMap.Lazy , HS.keysSet ) where -import Data.HashMap.Internal as HM +import Data.HashMap.Internal +import Prelude () + import qualified Data.HashSet.Internal as HS -import Prelude () -- $strictness -- diff --git a/Data/HashMap/Strict.hs b/Data/HashMap/Strict.hs index 0ba674ec..c2e9263c 100644 --- a/Data/HashMap/Strict.hs +++ b/Data/HashMap/Strict.hs @@ -105,9 +105,10 @@ module Data.HashMap.Strict , HS.keysSet ) where -import Data.HashMap.Internal.Strict as HM +import Data.HashMap.Internal.Strict +import Prelude () + import qualified Data.HashSet.Internal as HS -import Prelude () -- $strictness -- diff --git a/Data/HashSet.hs b/Data/HashSet.hs index 88cc3bec..dfa95d86 100644 --- a/Data/HashSet.hs +++ b/Data/HashSet.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE Safe #-} ------------------------------------------------------------------------ @@ -137,4 +137,4 @@ module Data.HashSet ) where import Data.HashSet.Internal -import Prelude () +import Prelude () diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs index 2676d6ce..9b628d02 100644 --- a/Data/HashSet/Internal.hs +++ b/Data/HashSet/Internal.hs @@ -1,9 +1,9 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DeriveLift #-} -{-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveLift #-} +{-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_HADDOCK not-home #-} ------------------------------------------------------------------------ @@ -90,25 +90,22 @@ module Data.HashSet.Internal , keysSet ) where -import Control.DeepSeq (NFData(..)) -import Data.Data +import Control.DeepSeq (NFData (..), NFData1 (..), liftRnf2) +import Data.Data (Constr, Data (..), DataType) import Data.Functor.Classes -import Data.HashMap.Internal - ( HashMap, foldMapWithKey, foldlWithKey, foldrWithKey - , equalKeys, equalKeys1) -import Data.Hashable (Hashable(hashWithSalt)) -import Data.Semigroup (Semigroup(..), stimesIdempotentMonoid) -import GHC.Exts (build) -import qualified GHC.Exts as Exts -import Prelude hiding (filter, foldr, foldl, map, null) -import qualified Data.Foldable as Foldable -import qualified Data.HashMap.Internal as H -import qualified Data.List as List +import Data.HashMap.Internal (HashMap, equalKeys, equalKeys1, foldMapWithKey, + foldlWithKey, foldrWithKey) +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Hashable.Lifted (Hashable1 (..), Hashable2 (..)) +import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid) +import Prelude hiding (filter, foldl, foldr, map, null) import Text.Read -import qualified Data.Hashable.Lifted as H - -import qualified Control.DeepSeq as NF +import qualified Data.Data as Data +import qualified Data.Foldable as Foldable +import qualified Data.HashMap.Internal as H +import qualified Data.List as List +import qualified GHC.Exts as Exts import qualified Language.Haskell.TH.Syntax as TH -- | A set of values. A set cannot contain duplicate values. @@ -126,8 +123,8 @@ instance (NFData a) => NFData (HashSet a) where {-# INLINE rnf #-} -- | @since 0.2.14.0 -instance NF.NFData1 HashSet where - liftRnf rnf1 = NF.liftRnf2 rnf1 rnf . asMap +instance NFData1 HashSet where + liftRnf rnf1 = liftRnf2 rnf1 rnf . asMap -- | Note that, in the presence of hash collisions, equal @HashSet@s may -- behave differently, i.e. substitutivity may be violated: @@ -233,23 +230,23 @@ instance (Show a) => Show (HashSet a) where instance (Data a, Eq a, Hashable a) => Data (HashSet a) where gfoldl f z m = z fromList `f` toList m toConstr _ = fromListConstr - gunfold k z c = case constrIndex c of + gunfold k z c = case Data.constrIndex c of 1 -> k (z fromList) _ -> error "gunfold" dataTypeOf _ = hashSetDataType - dataCast1 f = gcast1 f + dataCast1 f = Data.gcast1 f -instance H.Hashable1 HashSet where - liftHashWithSalt h s = H.liftHashWithSalt2 h hashWithSalt s . asMap +instance Hashable1 HashSet where + liftHashWithSalt h s = liftHashWithSalt2 h hashWithSalt s . asMap instance (Hashable a) => Hashable (HashSet a) where hashWithSalt salt = hashWithSalt salt . asMap fromListConstr :: Constr -fromListConstr = mkConstr hashSetDataType "fromList" [] Prefix +fromListConstr = Data.mkConstr hashSetDataType "fromList" [] Data.Prefix hashSetDataType :: DataType -hashSetDataType = mkDataType "Data.HashSet.Internal.HashSet" [fromListConstr] +hashSetDataType = Data.mkDataType "Data.HashSet.Internal.HashSet" [fromListConstr] -- | /O(1)/ Construct an empty set. -- @@ -445,7 +442,7 @@ filter p = HashSet . H.filterWithKey q . asMap -- | /O(n)/ Return a list of this set's elements. The list is -- produced lazily. toList :: HashSet a -> [a] -toList t = build (\ c z -> foldrWithKey ((const .) c) z (asMap t)) +toList t = Exts.build (\ c z -> foldrWithKey ((const .) c) z (asMap t)) {-# INLINE toList #-} -- | /O(n*min(W, n))/ Construct a set from a list of elements. diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs index 486fda58..8f148035 100644 --- a/benchmarks/Benchmarks.hs +++ b/benchmarks/Benchmarks.hs @@ -1,25 +1,30 @@ -{-# LANGUAGE CPP, DeriveAnyClass, DeriveGeneric, GADTs, PackageImports, RecordWildCards #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE PackageImports #-} +{-# LANGUAGE RecordWildCards #-} module Main where -import Control.DeepSeq -import Data.Bits ((.&.)) -import Data.Functor.Identity -import Data.Hashable (Hashable, hash) -import qualified Data.ByteString as BS +import Control.DeepSeq (NFData (..)) +import Data.Bits ((.&.)) +import Data.Functor.Identity (Identity (..)) +import Data.Hashable (Hashable, hash) +import Data.List (foldl') +import Data.Maybe (fromMaybe) +import GHC.Generics (Generic) +import Prelude hiding (lookup) +import Test.Tasty.Bench (bench, bgroup, defaultMain, env, nf, whnf) + +import qualified Data.ByteString as BS import qualified "hashmap" Data.HashMap as IHM -import qualified Data.HashMap.Strict as HM -import qualified Data.IntMap as IM -import qualified Data.Map as M -import Data.List (foldl') -import Data.Maybe (fromMaybe) -import GHC.Generics (Generic) -import Prelude hiding (lookup) -import Test.Tasty.Bench (bench, bgroup, defaultMain, env, nf, whnf) - -import qualified Util.ByteString as UBS -import qualified Util.Int as UI -import qualified Util.String as US +import qualified Data.HashMap.Strict as HM +import qualified Data.IntMap as IM +import qualified Data.Map as M +import qualified Util.ByteString as UBS +import qualified Util.Int as UI +import qualified Util.String as US data B where B :: NFData a => a -> B diff --git a/benchmarks/Util/ByteString.hs b/benchmarks/Util/ByteString.hs index 6359889b..45eb9aab 100644 --- a/benchmarks/Util/ByteString.hs +++ b/benchmarks/Util/ByteString.hs @@ -2,10 +2,9 @@ -- random 'ByteString's. module Util.ByteString where -import qualified Data.ByteString as S +import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as C - -import Util.String as String +import qualified Util.String as String -- | Generate a number of fixed length 'ByteString's where the content -- of the strings are letters in ascending order. diff --git a/tests/Main.hs b/tests/Main.hs index c18ae77d..9e337ad2 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -2,8 +2,8 @@ module Main (main) where import Test.Tasty (defaultMain, testGroup) -import qualified Regressions import qualified Properties +import qualified Regressions import qualified Strictness main :: IO () diff --git a/tests/Properties/HashMapLazy.hs b/tests/Properties/HashMapLazy.hs index b783c4f1..8b712da3 100644 --- a/tests/Properties/HashMapLazy.hs +++ b/tests/Properties/HashMapLazy.hs @@ -1,38 +1,44 @@ -{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- because of Arbitrary (HashMap k v) -- | Tests for the 'Data.HashMap.Lazy' module. We test functions by -- comparing them to @Map@ from @containers@. #if defined(STRICT) -module Properties.HashMapStrict (tests) where +#define MODULE_NAME Properties.HashMapStrict #else -module Properties.HashMapLazy (tests) where +#define MODULE_NAME Properties.HashMapLazy #endif -import Control.Monad ( guard ) -import qualified Data.Foldable as Foldable +module MODULE_NAME (tests) where + +import Control.Applicative (Const (..)) +import Control.Monad (guard) import Data.Bifoldable -import Data.Function (on) -import Data.Hashable (Hashable(hashWithSalt)) -import qualified Data.List as L -import Data.Ord (comparing) +import Data.Function (on) +import Data.Functor.Identity (Identity (..)) +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Ord (comparing) +import Test.QuickCheck (Arbitrary (..), Property, elements, forAll, + (===), (==>)) +import Test.QuickCheck.Function (Fun, apply) +import Test.QuickCheck.Poly (A, B) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.QuickCheck (testProperty) + +import qualified Data.Foldable as Foldable +import qualified Data.List as List + #if defined(STRICT) -import Data.HashMap.Strict (HashMap) +import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM -import qualified Data.Map.Strict as M +import qualified Data.Map.Strict as M #else -import Data.HashMap.Lazy (HashMap) +import Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as HM -import qualified Data.Map.Lazy as M +import qualified Data.Map.Lazy as M #endif -import Test.QuickCheck (Arbitrary(..), Property, (==>), (===), forAll, elements) -import Test.Tasty (TestTree, testGroup) -import Test.Tasty.QuickCheck (testProperty) -import Data.Functor.Identity (Identity (..)) -import Control.Applicative (Const (..)) -import Test.QuickCheck.Function (Fun, apply) -import Test.QuickCheck.Poly (A, B) -- Key type that generates more hash collisions. newtype Key = K { unK :: Int } @@ -102,22 +108,22 @@ pFunctor :: [(Key, Int)] -> Bool pFunctor = fmap (+ 1) `eq_` fmap (+ 1) pFoldable :: [(Int, Int)] -> Bool -pFoldable = (L.sort . Foldable.foldr (:) []) `eq` - (L.sort . Foldable.foldr (:) []) +pFoldable = (List.sort . Foldable.foldr (:) []) `eq` + (List.sort . Foldable.foldr (:) []) pHashable :: [(Key, Int)] -> [Int] -> Int -> Property pHashable xs is salt = x == y ==> hashWithSalt salt x === hashWithSalt salt y where - xs' = L.nubBy (\(k,_) (k',_) -> k == k') xs + xs' = List.nubBy (\(k,_) (k',_) -> k == k') xs ys = shuffle is xs' x = HM.fromList xs' y = HM.fromList ys -- Shuffle the list using indexes in the second shuffle :: [Int] -> [a] -> [a] - shuffle idxs = L.map snd - . L.sortBy (comparing fst) - . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + shuffle idxs = List.map snd + . List.sortBy (comparing fst) + . List.zip (idxs ++ [List.maximum (0:is) + 1 ..]) ------------------------------------------------------------------------ -- ** Basic interface @@ -292,8 +298,8 @@ pMap = M.map (+ 1) `eq_` HM.map (+ 1) pTraverse :: [(Key, Int)] -> Bool pTraverse xs = - L.sort (fmap (L.sort . M.toList) (M.traverseWithKey (\_ v -> [v + 1, v + 2]) (M.fromList (take 10 xs)))) - == L.sort (fmap (L.sort . HM.toList) (HM.traverseWithKey (\_ v -> [v + 1, v + 2]) (HM.fromList (take 10 xs)))) + List.sort (fmap (List.sort . M.toList) (M.traverseWithKey (\_ v -> [v + 1, v + 2]) (M.fromList (take 10 xs)))) + == List.sort (fmap (List.sort . HM.toList) (HM.traverseWithKey (\_ v -> [v + 1, v + 2]) (HM.fromList (take 10 xs)))) pMapKeys :: [(Int, Int)] -> Bool pMapKeys = M.mapKeys (+1) `eq_` HM.mapKeys (+1) @@ -330,10 +336,10 @@ pIntersectionWithKey xs ys = M.intersectionWithKey go (M.fromList xs) `eq_` -- ** Folds pFoldr :: [(Int, Int)] -> Bool -pFoldr = (L.sort . M.foldr (:) []) `eq` (L.sort . HM.foldr (:) []) +pFoldr = (List.sort . M.foldr (:) []) `eq` (List.sort . HM.foldr (:) []) pFoldl :: [(Int, Int)] -> Bool -pFoldl = (L.sort . M.foldl (flip (:)) []) `eq` (L.sort . HM.foldl (flip (:)) []) +pFoldl = (List.sort . M.foldl (flip (:)) []) `eq` (List.sort . HM.foldl (flip (:)) []) pBifoldMap :: [(Int, Int)] -> Bool pBifoldMap xs = concatMap f (HM.toList m) == bifoldMap (:[]) (:[]) m @@ -376,10 +382,10 @@ pFoldlWithKey' = (sortByKey . M.foldlWithKey' f []) `eq` where f z k v = (k, v) : z pFoldl' :: [(Int, Int)] -> Bool -pFoldl' = (L.sort . M.foldl' (flip (:)) []) `eq` (L.sort . HM.foldl' (flip (:)) []) +pFoldl' = (List.sort . M.foldl' (flip (:)) []) `eq` (List.sort . HM.foldl' (flip (:)) []) pFoldr' :: [(Int, Int)] -> Bool -pFoldr' = (L.sort . M.foldr' (:) []) `eq` (L.sort . HM.foldr' (:) []) +pFoldr' = (List.sort . M.foldr' (:) []) `eq` (List.sort . HM.foldr' (:) []) ------------------------------------------------------------------------ -- ** Filter @@ -432,10 +438,10 @@ pToList :: [(Key, Int)] -> Bool pToList = M.toAscList `eq` toAscList pElems :: [(Key, Int)] -> Bool -pElems = (L.sort . M.elems) `eq` (L.sort . HM.elems) +pElems = (List.sort . M.elems) `eq` (List.sort . HM.elems) pKeys :: [(Key, Int)] -> Bool -pKeys = (L.sort . M.keys) `eq` (L.sort . HM.keys) +pKeys = (List.sort . M.keys) `eq` (List.sort . HM.keys) ------------------------------------------------------------------------ -- * Test list @@ -579,7 +585,7 @@ infix 4 `eq_` -- * Helpers sortByKey :: Ord k => [(k, v)] -> [(k, v)] -sortByKey = L.sortBy (compare `on` fst) +sortByKey = List.sortBy (compare `on` fst) toAscList :: Ord k => HM.HashMap k v -> [(k, v)] -toAscList = L.sortBy (compare `on` fst) . HM.toList +toAscList = List.sortBy (compare `on` fst) . HM.toList diff --git a/tests/Properties/HashSet.hs b/tests/Properties/HashSet.hs index 5564057b..6af5d5fb 100644 --- a/tests/Properties/HashSet.hs +++ b/tests/Properties/HashSet.hs @@ -1,20 +1,22 @@ -{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | Tests for the 'Data.HashSet' module. We test functions by -- comparing them to @Set@ from @containers@. module Properties.HashSet (tests) where -import qualified Data.Foldable as Foldable -import Data.Hashable (Hashable(hashWithSalt)) -import qualified Data.List as L -import qualified Data.HashSet as S -import qualified Data.Set as Set -import Data.Ord (comparing) -import Test.QuickCheck (Arbitrary, Property, (==>), (===)) -import Test.Tasty (TestTree, testGroup) +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Ord (comparing) +import Test.QuickCheck (Arbitrary, Property, (===), (==>)) +import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) +import qualified Data.Foldable as Foldable +import qualified Data.HashSet as S +import qualified Data.List as List +import qualified Data.Set as Set + -- Key type that generates more hash collisions. newtype Key = K { unK :: Int } deriving (Arbitrary, Enum, Eq, Integral, Num, Ord, Read, Show, Real) @@ -77,28 +79,28 @@ pReadShow :: [Key] -> Bool pReadShow xs = Set.fromList xs == read (show (Set.fromList xs)) pFoldable :: [Int] -> Bool -pFoldable = (L.sort . Foldable.foldr (:) []) `eq` - (L.sort . Foldable.foldr (:) []) +pFoldable = (List.sort . Foldable.foldr (:) []) `eq` + (List.sort . Foldable.foldr (:) []) pPermutationEq :: [Key] -> [Int] -> Bool pPermutationEq xs is = S.fromList xs == S.fromList ys where ys = shuffle is xs - shuffle idxs = L.map snd - . L.sortBy (comparing fst) - . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + shuffle idxs = List.map snd + . List.sortBy (comparing fst) + . List.zip (idxs ++ [List.maximum (0:is) + 1 ..]) pHashable :: [Key] -> [Int] -> Int -> Property pHashable xs is salt = x == y ==> hashWithSalt salt x === hashWithSalt salt y where - xs' = L.nub xs + xs' = List.nub xs ys = shuffle is xs' x = S.fromList xs' y = S.fromList ys - shuffle idxs = L.map snd - . L.sortBy (comparing fst) - . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + shuffle idxs = List.map snd + . List.sortBy (comparing fst) + . List.zip (idxs ++ [List.maximum (0:is) + 1 ..]) ------------------------------------------------------------------------ -- ** Basic interface @@ -132,8 +134,8 @@ pMap = Set.map (+ 1) `eq_` S.map (+ 1) -- ** Folds pFoldr :: [Int] -> Bool -pFoldr = (L.sort . foldrSet (:) []) `eq` - (L.sort . S.foldr (:) []) +pFoldr = (List.sort . foldrSet (:) []) `eq` + (List.sort . S.foldr (:) []) foldrSet :: (a -> b -> b) -> b -> Set.Set a -> b foldrSet = Set.foldr @@ -231,4 +233,4 @@ eq_ f g = (Set.toAscList . f) `eq` (toAscList . g) -- * Helpers toAscList :: Ord a => S.HashSet a -> [a] -toAscList = L.sort . S.toList +toAscList = List.sort . S.toList diff --git a/tests/Properties/List.hs b/tests/Properties/List.hs index 1e3f87ba..b4294783 100644 --- a/tests/Properties/List.hs +++ b/tests/Properties/List.hs @@ -1,12 +1,11 @@ module Properties.List (tests) where import Data.HashMap.Internal.List -import Data.List (nub, sort, sortBy) -import Data.Ord (comparing) - -import Test.Tasty (TestTree, testGroup) -import Test.Tasty.QuickCheck (testProperty) -import Test.QuickCheck ((==>), (===), property, Property) +import Data.List (nub, sort, sortBy) +import Data.Ord (comparing) +import Test.QuickCheck (Property, property, (===), (==>)) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.QuickCheck (testProperty) tests :: TestTree tests = testGroup "Data.HashMap.Internal.List" diff --git a/tests/Regressions.hs b/tests/Regressions.hs index 51d72ad9..808a96e3 100644 --- a/tests/Regressions.hs +++ b/tests/Regressions.hs @@ -1,32 +1,33 @@ +{-# LANGUAGE MagicHash #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE UnboxedTuples #-} module Regressions (tests) where -import Control.Exception (evaluate) -import Control.Monad (replicateM) -import Data.Hashable (Hashable(..)) -import qualified Data.HashMap.Strict as HM -import qualified Data.HashMap.Lazy as HML -import Data.List (delete) -import Data.Maybe -import GHC.Exts (touch#) -import GHC.IO (IO (..)) -import System.Mem (performGC) -import System.Mem.Weak (mkWeakPtr, deRefWeak) -import System.Random (randomIO) -import Test.HUnit (Assertion, assert) -import Test.Tasty (TestTree, testGroup) -import Test.Tasty.HUnit (testCase) -import Test.Tasty.QuickCheck (testProperty) +import Control.Exception (evaluate) +import Control.Monad (replicateM) +import Data.Hashable (Hashable (..)) +import Data.List (delete) +import Data.Maybe (isJust, isNothing) +import GHC.Exts (touch#) +import GHC.IO (IO (..)) +import System.Mem (performGC) +import System.Mem.Weak (deRefWeak, mkWeakPtr) +import System.Random (randomIO) +import Test.HUnit (Assertion, assert) import Test.QuickCheck +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase) +import Test.Tasty.QuickCheck (testProperty) + +import qualified Data.HashMap.Lazy as HML +import qualified Data.HashMap.Strict as HMS issue32 :: Assertion -issue32 = assert $ isJust $ HM.lookup 7 m' +issue32 = assert $ isJust $ HMS.lookup 7 m' where ns = [0..16] :: [Int] - m = HM.fromList (zip ns (repeat [])) - m' = HM.delete 10 m + m = HMS.fromList (zip ns (repeat [])) + m' = HMS.delete 10 m ------------------------------------------------------------------------ -- Issue #39 @@ -36,8 +37,8 @@ issue32 = assert $ isJust $ HM.lookup 7 m' issue39 :: Assertion issue39 = assert $ hm1 == hm2 where - hm1 = HM.fromList ([a, b] `zip` [1, 1 :: Int ..]) - hm2 = HM.fromList ([b, a] `zip` [1, 1 :: Int ..]) + hm1 = HMS.fromList ([a, b] `zip` [1, 1 :: Int ..]) + hm2 = HMS.fromList ([b, a] `zip` [1, 1 :: Int ..]) a = (1, -1) :: (Int, Int) b = (-1, 1) :: (Int, Int) @@ -76,10 +77,10 @@ propEqAfterDelete :: Keys -> Bool propEqAfterDelete (Keys keys) = let keyMap = mapFromKeys keys k = head keys - in HM.delete k keyMap == mapFromKeys (delete k keys) + in HMS.delete k keyMap == mapFromKeys (delete k keys) -mapFromKeys :: [Int] -> HM.HashMap Int () -mapFromKeys keys = HM.fromList (zip keys (repeat ())) +mapFromKeys :: [Int] -> HMS.HashMap Int () +mapFromKeys keys = HMS.fromList (zip keys (repeat ())) ------------------------------------------------------------------------ -- Issue #254 @@ -117,7 +118,7 @@ issue254Strict = do i :: Int <- randomIO let oldV = show i weakV <- mkWeakPtr oldV Nothing - mp <- evaluate $ HM.insert (KC 1) "3" $ HM.fromList [(KC 0, "1"), (KC 1, oldV)] + mp <- evaluate $ HMS.insert (KC 1) "3" $ HMS.fromList [(KC 0, "1"), (KC 1, oldV)] performGC res <- deRefWeak weakV touch mp diff --git a/tests/Strictness.hs b/tests/Strictness.hs index f80e1bb6..255851e9 100644 --- a/tests/Strictness.hs +++ b/tests/Strictness.hs @@ -1,21 +1,24 @@ -{-# LANGUAGE CPP, FlexibleInstances, GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Strictness (tests) where -import Data.Hashable (Hashable(hashWithSalt)) +import Control.Arrow (second) +import Control.Monad (guard) +import Data.Foldable (foldl') +import Data.HashMap.Strict (HashMap) +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Maybe (fromMaybe, isJust) import Test.ChasingBottoms.IsBottom -import Test.Tasty (TestTree, testGroup) -import Test.Tasty.QuickCheck (testProperty) -import Test.QuickCheck (Arbitrary(arbitrary), Property, (===), (.&&.)) +import Test.QuickCheck (Arbitrary (arbitrary), Property, (.&&.), + (===)) import Test.QuickCheck.Function -import Test.QuickCheck.Poly (A) -import Data.Maybe (fromMaybe, isJust) -import Control.Arrow (second) -import Control.Monad (guard) -import Data.Foldable (foldl') +import Test.QuickCheck.Poly (A) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.QuickCheck (testProperty) -import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM -- Key type that generates more hash collisions. diff --git a/utils/Stats.hs b/utils/Stats.hs index c0150c82..7278ecc3 100644 --- a/utils/Stats.hs +++ b/utils/Stats.hs @@ -1,13 +1,14 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -funbox-strict-fields #-} module Stats where -import qualified Data.HashMap.Internal.Array as A -import Data.HashMap.Internal (HashMap(..)) -import qualified Data.HashMap.Internal as HM +import Data.HashMap.Internal (HashMap (..)) import Data.Semigroup +import qualified Data.HashMap.Internal as HM +import qualified Data.HashMap.Internal.Array as A + data Histogram = H { empty :: !Int , leaf :: !Int From d4e31f6169bfb31a4c539066f0f47e2c0889cd51 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 6 Mar 2022 16:09:51 +0100 Subject: [PATCH 21/21] Prepare release 0.2.17.0 (#369) --- CHANGES.md | 8 ++++++++ Data/HashMap/Internal/Array.hs | 1 + unordered-containers.cabal | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ca5e7f1b..626c6f2d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,14 @@ * [Expose internal constructors for `HashSet`, `Array` and `MArray`](https://github.com/haskell-unordered-containers/unordered-containers/pull/347) +* [Tweak internal `Array.insertM` function](https://github.com/haskell-unordered-containers/unordered-containers/pull/359) + +* [Drop support for GHC 8.0](https://github.com/haskell-unordered-containers/unordered-containers/pull/354) + +* [Drop support for `hashable < 1.2.5`](https://github.com/haskell-unordered-containers/unordered-containers/pull/355) + +* Various cleanup and documentation improvements + [0.2.17.0]: https://github.com/haskell-unordered-containers/unordered-containers/compare/v0.2.16.0...v0.2.17.0 ## [0.2.16.0] diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs index 78059ba9..6176015b 100644 --- a/Data/HashMap/Internal/Array.hs +++ b/Data/HashMap/Internal/Array.hs @@ -489,6 +489,7 @@ fromList' n xs0 = go (!x:xs) mary i = do write mary i x go xs mary (i+1) +-- | @since 0.2.17.0 instance TH.Lift a => TH.Lift (Array a) where #if MIN_VERSION_template_haskell(2,16,0) liftTyped ar = [|| fromList' arlen arlist ||] diff --git a/unordered-containers.cabal b/unordered-containers.cabal index 755649a0..363ab3dc 100644 --- a/unordered-containers.cabal +++ b/unordered-containers.cabal @@ -1,5 +1,5 @@ name: unordered-containers -version: 0.2.16.0 +version: 0.2.17.0 synopsis: Efficient hashing-based container types description: Efficient hashing-based container types. The containers have been