packages feed

monoidmap-internal 0.1.2.0 → 0.2.0.0

raw patch · 7 files changed

+188/−1031 lines, 7 filesdep −tasty-hunitPVP ok

version bump matches the API change (PVP)

Dependencies removed: tasty-hunit

API changes (from Hackage documentation)

- Data.MonoidMap.Internal.RecoveredMap: data Map k v
- Data.MonoidMap.Internal.RecoveredMap: delete :: Ord k => k -> Map k v -> Map k v
- Data.MonoidMap.Internal.RecoveredMap: empty :: Map k v
- Data.MonoidMap.Internal.RecoveredMap: fromList :: Ord k => [(k, v)] -> Map k v
- Data.MonoidMap.Internal.RecoveredMap: insert :: Ord k => k -> v -> Map k v -> Map k v
- Data.MonoidMap.Internal.RecoveredMap: instance (Control.DeepSeq.NFData k, Control.DeepSeq.NFData v) => Control.DeepSeq.NFData (Data.MonoidMap.Internal.RecoveredMap.Map k v)
- Data.MonoidMap.Internal.RecoveredMap: instance (GHC.Classes.Eq k, GHC.Classes.Eq v) => GHC.Classes.Eq (Data.MonoidMap.Internal.RecoveredMap.Map k v)
- Data.MonoidMap.Internal.RecoveredMap: instance (GHC.Show.Show k, GHC.Show.Show v) => GHC.Show.Show (Data.MonoidMap.Internal.RecoveredMap.Map k v)
- Data.MonoidMap.Internal.RecoveredMap: instance GHC.Base.Functor (Data.MonoidMap.Internal.RecoveredMap.Map k)
- Data.MonoidMap.Internal.RecoveredMap: instance GHC.Classes.Ord k => GHC.Base.Monoid (Data.MonoidMap.Internal.RecoveredMap.Map k v)
- Data.MonoidMap.Internal.RecoveredMap: instance GHC.Classes.Ord k => GHC.Base.Semigroup (Data.MonoidMap.Internal.RecoveredMap.Map k v)
- Data.MonoidMap.Internal.RecoveredMap: keysSet :: Map k v -> Set k
- Data.MonoidMap.Internal.RecoveredMap: lookup :: Ord k => k -> Map k v -> Maybe v
- Data.MonoidMap.Internal.RecoveredMap: map :: (v1 -> v2) -> Map k v1 -> Map k v2
- Data.MonoidMap.Internal.RecoveredMap: mapAccumL :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
- Data.MonoidMap.Internal.RecoveredMap: mapAccumLWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
- Data.MonoidMap.Internal.RecoveredMap: mapAccumR :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
- Data.MonoidMap.Internal.RecoveredMap: mapAccumRWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)
- Data.MonoidMap.Internal.RecoveredMap: mapWithKey :: (k -> v1 -> v2) -> Map k v1 -> Map k v2
- Data.MonoidMap.Internal.RecoveredMap: member :: Ord k => k -> Map k v -> Bool
- Data.MonoidMap.Internal.RecoveredMap: singleton :: Ord k => k -> v -> Map k v
- Data.MonoidMap.Internal.RecoveredMap: toList :: Map k v -> [(k, v)]

Files

CHANGELOG.md view
@@ -1,3 +1,15 @@+# 0.2.0.0++- Changed the role of the value type parameter of `MonoidMap` from+  `representational` to `nominal`. This prevents coercions between+  `MonoidMap` types with different value types, which could produce maps+  that contain `mempty` values.++  This is a breaking change: code that performs such coercions will no+  longer compile.++- Moved the `RecoveredMap` module to the `monoidmap-examples` package.+ # 0.1.2.0  - Improved the performance of `fromMap`, particularly for maps that
− components/monoidmap-benchmark/Main.hs
@@ -1,314 +0,0 @@--- |--- Copyright: © 2022–2026 Jonathan Knowles--- License: Apache-2.0------ Benchmark for the 'MonoidMap' type and its functions.------ When a 'MonoidMap' function has a direct counterpart in 'Map' (such as the--- 'fromList' function), we benchmark 'Map' against 'RecoveredMap': a newtype--- wrapper around 'MonoidMap' with functions that have the same semantics as--- those of 'Map', so that both sides of each comparison behave identically.------ When a 'MonoidMap' function has no direct counterpart in 'Map' (such as the--- 'fromMap' function), we benchmark the 'MonoidMap' function directly.----module Main where--import Control.DeepSeq-    ( NFData, rnf )-import Control.Exception-    ( evaluate )-import Data.Bool-    ( Bool (False, True) )-import Data.Eq-    ( Eq ((==)) )-import Data.Function-    ( const, flip, ($) )-import Data.Int-    ( Int )-import Data.List-    ( filter, foldl', zip )-import Data.Maybe-    ( Maybe, fromMaybe )-import Data.Monoid-    ( Monoid (mempty), Sum (Sum) )-import Data.Ord-    ( Ord )-import Data.Semigroup-    ( Semigroup ((<>)), stimes )-import Data.String-    ( String )-import Prelude-    ( Integer, Num, even, odd, (^), (+) )-import System.IO-    ( IO )-import Test.Tasty.Bench-    ( Benchmark, bcompare, bench, bgroup, defaultMain, nf )--import qualified Data.Map.Strict as OMap-import qualified Data.MonoidMap.Internal as MMap-import qualified Data.MonoidMap.Internal.RecoveredMap as RMap--main :: IO ()-main = do--    let om_natural = fromList elems_natural :: OMap.Map Int Int-        om_even    = fromList elems_even    :: OMap.Map Int Int-        om_odd     = fromList elems_odd     :: OMap.Map Int Int--        rm_natural = fromList elems_natural :: RMap.Map Int Int-        rm_even    = fromList elems_even    :: RMap.Map Int Int-        rm_odd     = fromList elems_odd     :: RMap.Map Int Int--        -- Maps in which different subsets of keys have 'null' values:-        om_null_none = mapWithNullIf (const False)   :: OMap.Map Int (Sum Int)-        om_null_min  = mapWithNullIf (== minNatural) :: OMap.Map Int (Sum Int)-        om_null_max  = mapWithNullIf (== maxNatural) :: OMap.Map Int (Sum Int)-        om_null_even = mapWithNullIf even            :: OMap.Map Int (Sum Int)-        om_null_odd  = mapWithNullIf odd             :: OMap.Map Int (Sum Int)-        om_null_all  = mapWithNullIf (const True)    :: OMap.Map Int (Sum Int)--    evaluate $ rnf [om_natural, om_even, om_odd]-    evaluate $ rnf [rm_natural, rm_even, rm_odd]-    evaluate $ rnf-        [ om_null_none-        , om_null_min-        , om_null_max-        , om_null_even-        , om_null_odd-        , om_null_all-        ]--    let fromMapCases =-            [ ("no nulls",           om_null_none)-            , ("one null (min key)", om_null_min)-            , ("one null (max key)", om_null_max)-            , ("even nulls",         om_null_even)-            , ("odd nulls",          om_null_odd)-            , ("all nulls",          om_null_all)-            ]--    defaultMain-        [ compareImplementations-            ("fromMap", MMap.fromMap) fromMapCases-            [ ( "fromMap_filter"-              , MMap.fromMap_filter-              )-            , ( "fromMap_filter_if_any_null"-              , MMap.fromMap_filter_if_any_null-              )-            , ( "fromMap_mapMaybe"-              , MMap.fromMap_mapMaybe-              )-            ]-        , compareImplementations-            ("fromMapWith", MMap.fromMapWith double) fromMapCases-            [ ( "fromMapWith_mapMaybe"-              , MMap.fromMapWith_mapMaybe double-              )-            , ( "fromMapWith_map_filter"-              , MMap.fromMapWith_map_filter double-              )-            , ( "fromMapWith_map_filter_if_any_null"-              , MMap.fromMapWith_map_filter_if_any_null double-              )-            ]-        , bgroup "delete"-            [ bgroup "absent"-                [ bench "Data.Map.Strict" $-                    nf (deleteMany evens) om_odd-                , bench "RecoveredMap" $-                    nf (deleteMany evens) rm_odd-                ]-            , bgroup "present"-                [ bench "Data.Map.Strict" $-                    nf (deleteMany evens) om_even-                , bench "RecoveredMap" $-                    nf (deleteMany evens) rm_even-                ]-            ]-        , bgroup "insert"-            [ bgroup "absent"-                [ bench "Data.Map.Strict" $-                    nf (insertMany elems_even) om_odd-                , bench "RecoveredMap" $-                    nf (insertMany elems_even) rm_odd-                ]-            , bgroup "present"-                [ bench "Data.Map.Strict" $-                    nf (insertMany elems_even) om_even-                , bench "RecoveredMap" $-                    nf (insertMany elems_even) rm_even-                ]-            ]-        , bgroup "lookup"-            [ bgroup "absent"-                [ bench "Data.Map.Strict" $-                    nf (lookupMany evens) om_odd-                , bench "RecoveredMap" $-                    nf (lookupMany evens) rm_odd-                ]-            , bgroup "present"-                [ bench "Data.Map.Strict" $-                    nf (lookupMany evens) om_even-                , bench "RecoveredMap" $-                    nf (lookupMany evens) rm_even-                ]-            ]-        , bgroup "mappend"-            [ bgroup "disjoint"-                [ bench "Data.Map.Strict" $-                    nf (<> om_even) om_odd-                , bench "RecoveredMap" $-                    nf (<> rm_even) rm_odd-                ]-            , bgroup "identical"-                [ bench "Data.Map.Strict" $-                    nf (<> om_even) om_even-                , bench "RecoveredMap" $-                    nf (<> rm_even) rm_even-                ]-            ]-        , bgroup "stimes"-            [ bench "Data.Map.Strict" $-                nf (stimes ten_power_24) om_natural-            , bench "RecoveredMap" $-                nf (stimes ten_power_24) rm_natural-            ]-        , bgroup "mapAccumL"-            [ bench "Data.Map.Strict" $-                nf (mapAccumL (\s v -> (s + v, v)) 0) om_natural-            , bench "RecoveredMap" $-                nf (mapAccumL (\s v -> (s + v, v)) 0) rm_natural-            ]-        , bgroup "mapAccumR"-            [ bench "Data.Map.Strict" $-                nf (mapAccumR (\s v -> (s + v, v)) 0) om_natural-            , bench "RecoveredMap" $-                nf (mapAccumR (\s v -> (s + v, v)) 0) rm_natural-            ]-        , bgroup "mapAccumLWithKey"-            [ bench "Data.Map.Strict" $-                nf (mapAccumL (\s v -> (s + v, v)) 0) om_natural-            , bench "RecoveredMap" $-                nf (mapAccumL (\s v -> (s + v, v)) 0) rm_natural-            ]-        , bgroup "mapAccumRWithKey"-            [ bench "Data.Map.Strict" $-                nf (mapAccumRWithKey (\s k v -> (s + k + v, v)) 0) om_natural-            , bench "RecoveredMap" $-                nf (mapAccumRWithKey (\s k v -> (s + k + v, v)) 0) rm_natural-            ]-        ]-  where-    minNatural :: Int-    minNatural = 1--    maxNatural :: Int-    maxNatural = 2 ^ (16 :: Int)--    elems_natural :: [(Int, Int)]-    elems_natural = zip naturals naturals--    elems_even :: [(Int, Int)]-    elems_even = zip evens evens--    elems_odd :: [(Int, Int)]-    elems_odd = zip odds odds--    naturals :: [Int]-    naturals = [minNatural .. maxNatural]--    evens :: [Int]-    evens = filter even naturals--    odds :: [Int]-    odds = filter odd naturals--    ten_power_24 :: Integer-    ten_power_24 = 1_000_000_000_000_000_000_000_000--    -- A map from keys [minNatural .. maxNatural], with a null value for each-    -- key that satisfies the given predicate, and a non-null value otherwise.-    mapWithNullIf :: (Int -> Bool) -> OMap.Map Int (Sum Int)-    mapWithNullIf hasNullValue = OMap.fromList-        [ (k, if hasNullValue k then mempty else Sum 1)-        | k <- [minNatural .. maxNatural]-        ]--    -- A simple function that replaces every value with a new value, while:-    -- - preserving existing null values; and-    -- - not introducing any new null values.-    double :: Semigroup m => m -> m-    double m = m <> m---- | Compares implementations of a public function with the public function---   itself, for each of the given cases.------ For each case, the time taken by each implementation is reported relative--- to the time taken by the public function.----compareImplementations-    :: NFData r-    => (String, i -> r)-    -- ^ The public function, with its name (also used as the group name).-    -> [(String, i)]-    -- ^ Cases (inputs) to benchmark, each with a label.-    -> [(String, i -> r)]-    -- ^ Implementations of the public function, each with a name.-    -> Benchmark-compareImplementations (publicName, public) cases implementations =-    bgroup publicName-        [ bgroup label $-            bench publicName (nf public input) :-            [ bcompare (relativeToPublic label) $ bench name $ nf f input-            | (name, f) <- implementations-            ]-        | (label, input) <- cases-        ]-  where-    -- Matches the benchmark of the public function for the given case (and-    -- nothing else):-    relativeToPublic label =-        "$NF == \"" <> publicName <> "\""-        <> " && $(NF-1) == \"" <> label <> "\""-        <> " && $(NF-2) == \"" <> publicName <> "\""--class Ord k => Map m k v where-    fromList :: [(k, v)] -> m k v-    delete :: k -> m k v -> m k v-    insert :: k -> v -> m k v -> m k v-    lookup :: k -> m k v -> Maybe v-    mapAccumL :: (s -> v -> (s, v)) -> s -> m k v -> (s, m k v)-    mapAccumR :: (s -> v -> (s, v)) -> s -> m k v -> (s, m k v)-    mapAccumLWithKey :: (s -> k -> v -> (s, v)) -> s -> m k v -> (s, m k v)-    mapAccumRWithKey :: (s -> k -> v -> (s, v)) -> s -> m k v -> (s, m k v)--instance Ord k => Map OMap.Map k v where-    fromList = OMap.fromList-    delete = OMap.delete-    insert = OMap.insert-    lookup = OMap.lookup-    mapAccumL = OMap.mapAccum-    mapAccumR f = OMap.mapAccumRWithKey (\s _ v -> f s v)-    mapAccumLWithKey = OMap.mapAccumWithKey-    mapAccumRWithKey = OMap.mapAccumRWithKey--instance (Ord k, Eq v) => Map RMap.Map k v where-    fromList = RMap.fromList-    delete = RMap.delete-    insert = RMap.insert-    lookup = RMap.lookup-    mapAccumL = RMap.mapAccumL-    mapAccumR = RMap.mapAccumR-    mapAccumLWithKey = RMap.mapAccumLWithKey-    mapAccumRWithKey = RMap.mapAccumRWithKey--deleteMany :: (Map m k v, Num v) => [k] -> m k v -> m k v-deleteMany xs m = foldl' (flip delete) m xs--insertMany :: (Map m k v, Num v) => [(k, v)] -> m k v -> m k v-insertMany xs m = foldl' (\m' (k, v) -> insert k v m') m xs--lookupMany :: (Map m k v, Num v) => [k] -> m k v -> v-lookupMany xs m = foldl' (\n k -> fromMaybe n (lookup k m)) 0 xs
+ components/monoidmap-internal-benchmark/Main.hs view
@@ -0,0 +1,147 @@+-- |+-- Copyright: © 2022–2026 Jonathan Knowles+-- License: Apache-2.0+--+-- Benchmark for internal implementations of 'MonoidMap' functions.+--+-- When a 'MonoidMap' function has alternative internal implementations (such+-- as the 'fromMap' function), we benchmark each alternative implementation+-- against the public function.+--+module Main where++import Control.DeepSeq+    ( NFData, rnf )+import Control.Exception+    ( evaluate )+import Data.Bool+    ( Bool (False, True) )+import Data.Eq+    ( Eq ((==)) )+import Data.Function+    ( const, ($) )+import Data.Int+    ( Int )+import Data.Monoid+    ( Monoid (mempty), Sum (Sum) )+import Data.Semigroup+    ( Semigroup ((<>)) )+import Data.String+    ( String )+import Prelude+    ( even, odd, (^) )+import System.IO+    ( IO )+import Test.Tasty.Bench+    ( Benchmark, bcompare, bench, bgroup, defaultMain, nf )++import qualified Data.Map.Strict as OMap+import qualified Data.MonoidMap.Internal as MMap++main :: IO ()+main = do++    let -- Maps in which different subsets of keys have 'null' values:+        om_null_none = mapWithNullIf (const False)   :: OMap.Map Int (Sum Int)+        om_null_min  = mapWithNullIf (== minNatural) :: OMap.Map Int (Sum Int)+        om_null_max  = mapWithNullIf (== maxNatural) :: OMap.Map Int (Sum Int)+        om_null_even = mapWithNullIf even            :: OMap.Map Int (Sum Int)+        om_null_odd  = mapWithNullIf odd             :: OMap.Map Int (Sum Int)+        om_null_all  = mapWithNullIf (const True)    :: OMap.Map Int (Sum Int)++    evaluate $ rnf+        [ om_null_none+        , om_null_min+        , om_null_max+        , om_null_even+        , om_null_odd+        , om_null_all+        ]++    let fromMapCases =+            [ ("no nulls",           om_null_none)+            , ("one null (min key)", om_null_min)+            , ("one null (max key)", om_null_max)+            , ("even nulls",         om_null_even)+            , ("odd nulls",          om_null_odd)+            , ("all nulls",          om_null_all)+            ]++    defaultMain+        [ compareImplementations+            ("fromMap", MMap.fromMap) fromMapCases+            [ ( "fromMap_filter"+              , MMap.fromMap_filter+              )+            , ( "fromMap_filter_if_any_null"+              , MMap.fromMap_filter_if_any_null+              )+            , ( "fromMap_mapMaybe"+              , MMap.fromMap_mapMaybe+              )+            ]+        , compareImplementations+            ("fromMapWith", MMap.fromMapWith double) fromMapCases+            [ ( "fromMapWith_mapMaybe"+              , MMap.fromMapWith_mapMaybe double+              )+            , ( "fromMapWith_map_filter"+              , MMap.fromMapWith_map_filter double+              )+            , ( "fromMapWith_map_filter_if_any_null"+              , MMap.fromMapWith_map_filter_if_any_null double+              )+            ]+        ]+  where+    minNatural :: Int+    minNatural = 1++    maxNatural :: Int+    maxNatural = 2 ^ (16 :: Int)++    -- A map from keys [minNatural .. maxNatural], with a null value for each+    -- key that satisfies the given predicate, and a non-null value otherwise.+    mapWithNullIf :: (Int -> Bool) -> OMap.Map Int (Sum Int)+    mapWithNullIf hasNullValue = OMap.fromList+        [ (k, if hasNullValue k then mempty else Sum 1)+        | k <- [minNatural .. maxNatural]+        ]++    -- A simple function that replaces every value with a new value, while:+    -- - preserving existing null values; and+    -- - not introducing any new null values.+    double :: Semigroup m => m -> m+    double m = m <> m++-- | Compares implementations of a public function with the public function+--   itself, for each of the given cases.+--+-- For each case, the time taken by each implementation is reported relative+-- to the time taken by the public function.+--+compareImplementations+    :: NFData r+    => (String, i -> r)+    -- ^ The public function, with its name (also used as the group name).+    -> [(String, i)]+    -- ^ Cases (inputs) to benchmark, each with a label.+    -> [(String, i -> r)]+    -- ^ Implementations of the public function, each with a name.+    -> Benchmark+compareImplementations (publicName, public) cases implementations =+    bgroup publicName+        [ bgroup label $+            bench publicName (nf public input) :+            [ bcompare (relativeToPublic label) $ bench name $ nf f input+            | (name, f) <- implementations+            ]+        | (label, input) <- cases+        ]+  where+    -- Matches the benchmark of the public function for the given case (and+    -- nothing else):+    relativeToPublic label =+        "$NF == \"" <> publicName <> "\""+        <> " && $(NF-1) == \"" <> label <> "\""+        <> " && $(NF-2) == \"" <> publicName <> "\""
components/monoidmap-internal/Data/MonoidMap/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE RoleAnnotations #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} {-# OPTIONS_GHC -fno-warn-unused-imports #-} {-# OPTIONS_HADDOCK not-home #-}@@ -262,6 +263,31 @@         via Map k     deriving (Eq2, Show2, Bifoldable)         via Map++-- Without an explicit type role annotation, the following roles would be+-- inferred:+--+--     type role MonoidMap nominal representational+--+-- These roles would be inherited from 'Map', which has these roles:+--+--     type role Map nominal representational+--+-- A representational role for the value type of a 'Map' is fine, because the+-- validity of a 'Map' does not depend on any property of its value type.+--+-- However, the validity of a 'MonoidMap' does depend on a property of its+-- value type: its 'mempty' value. A representational role would allow users+-- to coerce between value types with the same representation but different+-- 'mempty' values.+--+-- For example, users could coerce a 'MonoidMap' k ('Sum' 'Int') to a+-- 'MonoidMap' k ('Product' 'Int'), and thereby construct a map that contains+-- 'mempty' values.+--+-- So we must instead choose a nominal role for the value type of 'MonoidMap'.+--+type role MonoidMap nominal nominal  -- Internal alias used when extra brevity is required. type MM = MonoidMap
− components/monoidmap-internal/Data/MonoidMap/Internal/RecoveredMap.hs
@@ -1,125 +0,0 @@-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- |--- Copyright: © 2022–2026 Jonathan Knowles--- License: Apache-2.0------ An ordinary left-biased map similar to 'Map', implemented in terms of--- 'MonoidMap'.----module Data.MonoidMap.Internal.RecoveredMap-    ( Map-    , empty-    , singleton-    , fromList-    , toList-    , delete-    , insert-    , keysSet-    , lookup-    , member-    , map-    , mapWithKey-    , mapAccumL-    , mapAccumLWithKey-    , mapAccumR-    , mapAccumRWithKey-    )-    where--import Prelude hiding-    ( lookup, map )--import Control.DeepSeq-    ( NFData )-import Data.Coerce-    ( coerce )-import Data.Maybe-    ( mapMaybe )-import Data.Monoid-    ( First (..) )-import Data.MonoidMap.Internal-    ( MonoidMap )-import Data.Semigroup-    ( Semigroup (stimes), stimesIdempotentMonoid )-import Data.Set-    ( Set )--import qualified Data.MonoidMap.Internal as MonoidMap--newtype Map k v = Map-    --  'First' is used to mimic the left-biased nature of 'Data.Map':-    {unMap :: MonoidMap k (First v)}-    deriving newtype (Eq, NFData, Monoid)--instance Ord k => Semigroup (Map k v) where-    (<>) = coerce @(MonoidMap k (First v) -> _ -> _) (<>)-    stimes = stimesIdempotentMonoid--instance (Show k, Show v) => Show (Map k v) where-    show = ("fromList " <>) . show . toList--instance Functor (Map k) where-    fmap = map--empty :: Map k v-empty = Map MonoidMap.empty--singleton :: Ord k => k -> v -> Map k v-singleton k = Map . MonoidMap.singleton k . pure--fromList :: Ord k => [(k, v)] -> Map k v-fromList = Map . MonoidMap.fromListWith (const id) . fmap (fmap pure)--toList :: Map k v -> [(k, v)]-toList = mapMaybe (getFirst . sequenceA) . MonoidMap.toList . unMap--delete :: Ord k => k -> Map k v -> Map k v-delete k = Map . MonoidMap.nullify k . unMap--insert :: Ord k => k -> v -> Map k v -> Map k v-insert k v = Map . MonoidMap.set k (pure v) . unMap--keysSet :: Map k v -> Set k-keysSet = MonoidMap.nonNullKeys . unMap--lookup :: Ord k => k -> Map k v -> Maybe v-lookup k = getFirst . MonoidMap.get k . unMap--member :: Ord k => k -> Map k v -> Bool-member k = MonoidMap.nonNullKey k . unMap--map :: (v1 -> v2) -> Map k v1 -> Map k v2-map f = Map . MonoidMap.map (fmap f) . unMap--mapWithKey :: (k -> v1 -> v2) -> Map k v1 -> Map k v2-mapWithKey f = Map . MonoidMap.mapWithKey (fmap . f) . unMap--mapAccumL :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)-mapAccumL f s m = Map <$> MonoidMap.mapAccumL (accum f) s (unMap m)--mapAccumR :: (s -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)-mapAccumR f s m = Map <$> MonoidMap.mapAccumR (accum f) s (unMap m)--mapAccumLWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)-mapAccumLWithKey f s m =-    Map <$> MonoidMap.mapAccumLWithKey (accumWithKey f) s (unMap m)--mapAccumRWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> Map k v1 -> (s, Map k v2)-mapAccumRWithKey f s m =-    Map <$> MonoidMap.mapAccumRWithKey (accumWithKey f) s (unMap m)------------------------------------------------------------------------------------- Utilities-----------------------------------------------------------------------------------accum :: (s -> v1 -> (s, v2)) -> s -> First v1 -> (s, First v2)-accum f s1 (First mv1) = case mv1 of-    Just v1 -> let (s2, v2) = f s1 v1 in (s2, First (Just v2))-    Nothing -> (s1, First Nothing)--accumWithKey :: (s -> k -> v1 -> (s, v2)) -> s -> k -> First v1 -> (s, First v2)-accumWithKey f s1 k (First mv1) = case mv1 of-    Just v1 -> let (s2, v2) = f s1 k v1 in (s2, First (Just v2))-    Nothing -> (s1, First Nothing)
− components/monoidmap-test/Data/MonoidMap/Internal/RecoveredMapSpec.hs
@@ -1,584 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}-{-# HLINT ignore "Use any" #-}-{-# HLINT ignore "Use null" #-}---- |--- Copyright: © 2022–2026 Jonathan Knowles--- License: Apache-2.0----module Data.MonoidMap.Internal.RecoveredMapSpec-    where--import Prelude--import Data.Function-    ( on, (&) )-import Data.List-    ( nubBy )-import Data.Monoid-    ( Sum (..) )-import Data.Proxy-    ( Proxy (..) )-import Data.Semigroup-    ( Semigroup (stimes) )-import Data.Set-    ( Set )-import Data.Text-    ( Text )-import Data.Typeable-    ( Typeable, typeRep )-import Numeric.Natural-    ( Natural )-import Test.Common-    ()-import Test.Hspec-    ( Spec, describe, it )-import Test.QuickCheck-    ( Arbitrary (..)-    , CoArbitrary-    , Fun-    , Function-    , NonNegative (..)-    , Property-    , Testable-    , applyFun-    , applyFun2-    , applyFun3-    , checkCoverage-    , cover-    , listOf-    , shrinkMapBy-    , (===)-    )-import Test.QuickCheck.Classes-    ( eqLaws, functorLaws, monoidLaws, semigroupLaws, semigroupMonoidLaws )-import Test.Hspec.QuickCheck.Classes-    ( testLaws )--import qualified Data.Map.Strict as OMap-import qualified Data.Set as Set-import qualified Data.MonoidMap.Internal.RecoveredMap as RMap-import qualified Test.QuickCheck as QC--spec :: Spec-spec = do-    specFor (Proxy @Int) (Proxy @(Set Int))-    specFor (Proxy @Int) (Proxy @(Set Natural))-    specFor (Proxy @Int) (Proxy @(Sum Int))-    specFor (Proxy @Int) (Proxy @(Sum Natural))-    specFor (Proxy @Int) (Proxy @Text)--specFor-    :: forall k v. () =>-        ( Arbitrary k-        , Arbitrary v-        , CoArbitrary k-        , CoArbitrary v-        , Eq v-        , Function k-        , Function v-        , Monoid v-        , Ord k-        , Show k-        , Show v-        , Typeable k-        , Typeable v-        )-    => Proxy k-    -> Proxy v-    -> Spec-specFor keyType valueType = do--    let description = mconcat-            [ "RecoveredMap ("-            , show (typeRep keyType)-            , ") ("-            , show (typeRep valueType)-            , ")"-            ]--    let property :: Testable t => t -> Property-        property = checkCoverage . QC.property--    describe description $ do--        describe "Class laws" $ do-            testLaws @(RMap.Map k v)-                [ eqLaws-                , monoidLaws-                , semigroupLaws-                , semigroupMonoidLaws-                ]-            testLaws @(RMap.Map k)-                [ functorLaws-                ]--        describe "Conversion to and from lists" $ do-            it "prop_fromList_toList" $-                prop_fromList_toList-                    @k @v & property--        describe "Empty" $ do-            it "prop_empty_keysSet" $-                prop_empty_keysSet-                    @k & property-            it "prop_empty_lookup" $-                prop_empty_lookup-                    @k @v & property-            it "prop_empty_show" $-                prop_empty_show-                    @k @v & property-            it "prop_empty_toList" $-                prop_empty_toList-                    @k @v & property--        describe "Singleton" $ do-            it "prop_singleton_keysSet" $-                prop_singleton_keysSet-                    @k @v & property-            it "prop_singleton_lookup" $-                prop_singleton_lookup-                    @k @v & property-            it "prop_singleton_show" $-                prop_singleton_show-                    @k @v & property-            it "prop_singleton_toList" $-                prop_singleton_toList-                    @k @v & property--        describe "Append" $ do-            it "prop_append_toList" $-                prop_append_toList-                    @k @v & property--        describe "Times" $ do-            it "prop_stimes_toList" $-                prop_stimes_toList-                    @k @v & property--        describe "Delete" $ do-            it "prop_delete_lookup" $-                prop_delete_lookup-                    @k @v & property-            it "prop_delete_member" $-                prop_delete_member-                    @k @v & property-            it "prop_delete_toList" $-                prop_delete_toList-                    @k @v & property--        describe "Insert" $ do-            it "prop_insert_lookup" $-                prop_insert_lookup-                    @k @v & property-            it "prop_insert_member" $-                prop_insert_member-                    @k @v & property-            it "prop_insert_toList" $-                prop_insert_toList-                    @k @v & property--        describe "Map" $ do-            it "prop_map" $-                prop_map-                    @k @v @v & property-            it "prop_map_mempty" $-                prop_map_mempty-                    @k @v @v & property-            it "prop_mapWithKey" $-                prop_mapWithKey-                    @k @v @v & property--        describe "MapAccumL" $ do-            it "prop_mapAccumL @Int" $-                prop_mapAccumL @Int-                    @k @v @v & property-            it "prop_mapAccumL @String" $-                prop_mapAccumL @String-                    @k @v @v & property--        describe "MapAccumR" $ do-            it "prop_mapAccumR @Int" $-                prop_mapAccumR @Int-                    @k @v @v & property-            it "prop_mapAccumR @String" $-                prop_mapAccumR @String-                    @k @v @v & property--        describe "MapAccumWithKeyL" $ do-            it "prop_mapAccumLWithKey @Int" $-                prop_mapAccumLWithKey @Int-                    @k @v @v & property-            it "prop_mapAccumLWithKey @String" $-                prop_mapAccumLWithKey @String-                    @k @v @v & property--        describe "MapAccumWithKeyR" $ do-            it "prop_mapAccumRWithKey @Int" $-                prop_mapAccumRWithKey @Int-                    @k @v @v & property-            it "prop_mapAccumRWithKey @String" $-                prop_mapAccumRWithKey @String-                    @k @v @v & property------------------------------------------------------------------------------------- Conversion to and from lists-----------------------------------------------------------------------------------prop_fromList_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => [(k, v)]-    -> Property-prop_fromList_toList kvs =-    (===)-        (RMap.toList (RMap.fromList kvs))-        (OMap.toList (OMap.fromList kvs))-    & cover 10-        (length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs)-        "length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs"-    & cover 10-        (length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs)-        "length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs"------------------------------------------------------------------------------------- Empty-----------------------------------------------------------------------------------prop_empty_keysSet-    :: forall k. (Eq k, Show k)-    => Property-prop_empty_keysSet =-    (===)-        (RMap.keysSet (RMap.empty @k))-        (OMap.keysSet (OMap.empty @k))--prop_empty_lookup-    :: forall k v. (Ord k, Eq v, Show v)-    => k-    -> Property-prop_empty_lookup k =-    (===)-        (RMap.lookup k (RMap.empty @k @v))-        (OMap.lookup k (OMap.empty @k @v))--prop_empty_show-    :: forall k v. (Show k, Show v)-    => Property-prop_empty_show =-    (===)-        (show (RMap.empty @k @v))-        (show (OMap.empty @k @v))--prop_empty_toList-    :: forall k v. (Eq k, Show k, Eq v, Show v)-    => Property-prop_empty_toList =-    (===)-        (RMap.toList (RMap.empty @k @v))-        (OMap.toList (OMap.empty @k @v))------------------------------------------------------------------------------------- Singleton-----------------------------------------------------------------------------------prop_singleton_keysSet-    :: forall k v. (Ord k, Show k)-    => k-    -> v-    -> Property-prop_singleton_keysSet k v =-    (===)-        (RMap.keysSet (RMap.singleton k v))-        (OMap.keysSet (OMap.singleton k v))--prop_singleton_lookup-    :: forall k v. (Ord k, Eq v, Show v)-    => k-    -> v-    -> Property-prop_singleton_lookup k v =-    (===)-        (RMap.lookup k (RMap.singleton k v))-        (OMap.lookup k (OMap.singleton k v))--prop_singleton_show-    :: forall k v. (Ord k, Show k, Show v)-    => k-    -> v-    -> Property-prop_singleton_show k v =-    (===)-        (show (RMap.singleton k v))-        (show (OMap.singleton k v))--prop_singleton_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => k-    -> v-    -> Property-prop_singleton_toList k v =-    (===)-        (RMap.toList (RMap.singleton k v))-        (OMap.toList (OMap.singleton k v))------------------------------------------------------------------------------------- Append-----------------------------------------------------------------------------------prop_append_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => [(k, v)]-    -> [(k, v)]-    -> Property-prop_append_toList kvs1 kvs2 =-    (===)-        (RMap.toList (RMap.fromList kvs1 <> RMap.fromList kvs2))-        (OMap.toList (OMap.fromList kvs1 <> OMap.fromList kvs2))-    & cover 10-        (ks1 `Set.disjoint` ks2)-        "ks1 `Set.disjoint` ks2"-    & cover 10-        (not (ks1 `Set.disjoint` ks2))-        "not (ks1 `Set.disjoint` ks2)"-  where-    ks1 = Set.fromList (fst <$> kvs1)-    ks2 = Set.fromList (fst <$> kvs2)------------------------------------------------------------------------------------- Times-----------------------------------------------------------------------------------prop_stimes_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => [(k, v)]-    -> NonNegative Int-    -> Property-prop_stimes_toList kvs (NonNegative n) =-    (===)-        (RMap.toList (stimes n (RMap.fromList kvs)))-        (OMap.toList (stimes n (OMap.fromList kvs)))-    & cover 1-        (n == 0)-        "n == 0"-    & cover 1-        (n == 1)-        "n == 1"-    & cover 10-        (n >= 2)-        "n >= 2"------------------------------------------------------------------------------------- Delete-----------------------------------------------------------------------------------prop_delete_lookup-    :: forall k v. (Ord k, Eq v, Show v)-    => [(k, v)]-    -> k-    -> Property-prop_delete_lookup kvs k =-    (===)-        (RMap.lookup k (RMap.delete k (RMap.fromList kvs)))-        (OMap.lookup k (OMap.delete k (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"--prop_delete_member-    :: forall k v. (Ord k, Eq v)-    => [(k, v)]-    -> k-    -> Property-prop_delete_member kvs k =-    (===)-        (RMap.member k (RMap.delete k (RMap.fromList kvs)))-        (OMap.member k (OMap.delete k (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"--prop_delete_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => [(k, v)]-    -> k-    -> Property-prop_delete_toList kvs k =-    (===)-        (RMap.toList (RMap.delete k (RMap.fromList kvs)))-        (OMap.toList (OMap.delete k (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"------------------------------------------------------------------------------------- Insert-----------------------------------------------------------------------------------prop_insert_lookup-    :: forall k v. (Ord k, Eq v, Show v)-    => [(k, v)]-    -> k-    -> v-    -> Property-prop_insert_lookup kvs k v =-    (===)-        (RMap.lookup k (RMap.insert k v (RMap.fromList kvs)))-        (OMap.lookup k (OMap.insert k v (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"--prop_insert_member-    :: forall k v. (Ord k, Eq v)-    => [(k, v)]-    -> k-    -> v-    -> Property-prop_insert_member kvs k v =-    (===)-        (RMap.member k (RMap.insert k v (RMap.fromList kvs)))-        (OMap.member k (OMap.insert k v (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"--prop_insert_toList-    :: forall k v. (Ord k, Show k, Eq v, Show v)-    => [(k, v)]-    -> k-    -> v-    -> Property-prop_insert_toList kvs k v =-    (===)-        (RMap.toList (RMap.insert k v (RMap.fromList kvs)))-        (OMap.toList (OMap.insert k v (OMap.fromList kvs)))-    & cover 10-        (filter ((== k) . fst) kvs == [])-        "filter ((== k) . fst) kvs == []"-    & cover 10-        (filter ((== k) . fst) kvs /= [])-        "filter ((== k) . fst) kvs /= []"------------------------------------------------------------------------------------- Map-----------------------------------------------------------------------------------prop_map-    :: (Ord k, Show k, Eq v2, Show v2)-    => [(k, v1)]-    -> Fun v1 v2-    -> Property-prop_map kvs (applyFun -> f) =-    (===)-        (RMap.toList (RMap.map f (RMap.fromList kvs)))-        (OMap.toList (OMap.map f (OMap.fromList kvs)))--prop_map_mempty-    :: forall k v1 v2. (Ord k, Show k, Eq v2, Monoid v2, Show v2)-    => [(k, v1)]-    -> Property-prop_map_mempty kvs =-    (===)-        (RMap.toList (RMap.map (const (mempty @v2)) (RMap.fromList kvs)))-        (OMap.toList (OMap.map (const (mempty @v2)) (OMap.fromList kvs)))--prop_mapWithKey-    :: (Ord k, Show k, Eq v2, Show v2)-    => [(k, v1)]-    -> Fun (k, v1) v2-    -> Property-prop_mapWithKey kvs (applyFun2 -> f) =-    (===)-        (RMap.toList (RMap.mapWithKey f (RMap.fromList kvs)))-        (OMap.toList (OMap.mapWithKey f (OMap.fromList kvs)))------------------------------------------------------------------------------------- MapAccum-----------------------------------------------------------------------------------prop_mapAccumL-    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)-    => Fun (s, v1) (s, v2)-    -> s-    -> [(k, v1)]-    -> Property-prop_mapAccumL (applyFun2 -> f) s0 kvs =-    (===)-        (RMap.toList <$> rmapAccumL f s0 (RMap.fromList kvs))-        (OMap.toList <$> omapAccumL f s0 (OMap.fromList kvs))-  where-    rmapAccumL = RMap.mapAccumL-    omapAccumL = OMap.mapAccum--prop_mapAccumR-    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)-    => Fun (s, v1) (s, v2)-    -> s-    -> [(k, v1)]-    -> Property-prop_mapAccumR (applyFun2 -> f) s0 kvs =-    (===)-        (RMap.toList <$> rmapAccumR f s0 (RMap.fromList kvs))-        (OMap.toList <$> omapAccumR f s0 (OMap.fromList kvs))-  where-    rmapAccumR   = RMap.mapAccumR-    omapAccumR g = OMap.mapAccumRWithKey (\s _ v -> g s v)------------------------------------------------------------------------------------- MapAccumWithKey-----------------------------------------------------------------------------------prop_mapAccumLWithKey-    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)-    => Fun (s, k, v1) (s, v2)-    -> s-    -> [(k, v1)]-    -> Property-prop_mapAccumLWithKey (applyFun3 -> f) s0 kvs =-    (===)-        (RMap.toList <$> rmapAccumLWithKey f s0 (RMap.fromList kvs))-        (OMap.toList <$> omapAccumLWithKey f s0 (OMap.fromList kvs))-  where-    rmapAccumLWithKey = RMap.mapAccumLWithKey-    omapAccumLWithKey = OMap.mapAccumWithKey--prop_mapAccumRWithKey-    :: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)-    => Fun (s, k, v1) (s, v2)-    -> s-    -> [(k, v1)]-    -> Property-prop_mapAccumRWithKey (applyFun3 -> f) s0 kvs =-    (===)-        (RMap.toList <$> rmapAccumRWithKey f s0 (RMap.fromList kvs))-        (OMap.toList <$> omapAccumRWithKey f s0 (OMap.fromList kvs))-  where-    rmapAccumRWithKey = RMap.mapAccumRWithKey-    omapAccumRWithKey = OMap.mapAccumRWithKey------------------------------------------------------------------------------------- Arbitrary instances-----------------------------------------------------------------------------------instance (Arbitrary k, Ord k, Arbitrary v) =>-    Arbitrary (RMap.Map k v)-  where-    arbitrary = RMap.fromList <$> listOf ((,) <$> arbitrary <*> arbitrary)-    shrink = shrinkMapBy RMap.fromList RMap.toList shrink
monoidmap-internal.cabal view
@@ -1,6 +1,6 @@ cabal-version:  3.0 name:           monoidmap-internal-version:        0.1.2.0+version:        0.2.0.0 homepage:       https://github.com/jonathanknowles/monoidmap/tree/HEAD/packages/monoidmap-internal bug-reports:    https://github.com/jonathanknowles/monoidmap/issues license:        Apache-2.0@@ -46,8 +46,6 @@     build-depends:quickcheck-quid               >= 0.0.1.7    && < 0.1 common dependency-tasty-bench     build-depends:tasty-bench                   >= 0.3.2      && < 0.6-common dependency-tasty-hunit-    build-depends:tasty-hunit                   >= 0.10.0.3   && < 0.11 common dependency-text     build-depends:text                          >= 1.2.4.1    && < 2.2 @@ -89,18 +87,16 @@         components/monoidmap-internal     exposed-modules:         Data.MonoidMap.Internal-        Data.MonoidMap.Internal.RecoveredMap         Data.MonoidMap.Internal.Unsafe     default-language:         Haskell2010 -benchmark monoidmap-benchmark+benchmark monoidmap-internal-benchmark     import:       , dependency-base       , dependency-containers       , dependency-deepseq       , dependency-tasty-bench-      , dependency-tasty-hunit       , extensions     build-depends:       , monoidmap-internal@@ -109,7 +105,7 @@     type:         exitcode-stdio-1.0     hs-source-dirs:-        components/monoidmap-benchmark+        components/monoidmap-internal-benchmark     main-is:         Main.hs     -- Report allocation and memory statistics for each benchmark:@@ -155,7 +151,6 @@         Data.MonoidMap.Internal.MembershipSpec         Data.MonoidMap.Internal.PartitionSpec         Data.MonoidMap.Internal.PrefixSpec-        Data.MonoidMap.Internal.RecoveredMapSpec         Data.MonoidMap.Internal.SingletonSpec         Data.MonoidMap.Internal.SliceSpec         Data.MonoidMap.Internal.SuffixSpec