Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RULES for Data.IntMap.alterF #467

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Data/IntMap/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,13 @@ alterF f k m = (<$> f mv) $ \fres ->
Nothing -> maybe m (const (delete k m)) mv
Just v' -> insert k v' m
where mv = lookup k m
{-# INLINABLE [1] alterF #-}
#if MIN_VERSION_base(4,8,0)
{-# RULES
"Identity specialize alterF" forall (f :: Maybe a -> Identity (Maybe a)) k m.
alterF f k m = Identity $ alter (coerce f) k m
#-}
#endif

{--------------------------------------------------------------------
Union
Expand Down
76 changes: 76 additions & 0 deletions benchmarks/IntMap.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
module Main where

#if MIN_VERSION_base(4,9,0)
import Control.Applicative (Const(..))
#endif
import Control.DeepSeq (rnf)
import Control.Exception (evaluate)
import Criterion.Main (bench, defaultMain, whnf)
#if MIN_VERSION_base(4,8,0)
import Data.Coerce (coerce)
import Data.Functor.Identity (Identity (..))
#endif
import Data.List (foldl')
import qualified Data.IntMap as M
import qualified Data.IntMap.Strict as MS
Expand All @@ -12,6 +20,8 @@ import Prelude hiding (lookup)

main = do
let m = M.fromAscList elems :: M.IntMap Int
m_even = M.fromAscList elems_even :: M.IntMap Int
m_odd = M.fromAscList elems_odd :: M.IntMap Int
evaluate $ rnf [m]
defaultMain
[ bench "lookup" $ whnf (lookup keys) m
Expand All @@ -35,6 +45,26 @@ main = do
, bench "update" $ whnf (upd keys) m
, bench "updateLookupWithKey" $ whnf (upd' keys) m
, bench "alter" $ whnf (alt keys) m
, bench "alterF lookup absent" $ whnf (atLookup evens) m_odd
, bench "alterF lookup present" $ whnf (atLookup evens) m_even
, bench "alterF no rules lookup absent" $ whnf (atLookupNoRules evens) m_odd
, bench "alterF no rules lookup present" $ whnf (atLookupNoRules evens) m_even
, bench "alterF insert absent" $ whnf (atIns elems_even) m_odd
, bench "alterF insert present" $ whnf (atIns elems_even) m_even
, bench "alterF no rules insert absent" $ whnf (atInsNoRules elems_even) m_odd
, bench "alterF no rules insert present" $ whnf (atInsNoRules elems_even) m_even
, bench "alterF delete absent" $ whnf (atDel evens) m_odd
, bench "alterF delete present" $ whnf (atDel evens) m
, bench "alterF no rules delete absent" $ whnf (atDelNoRules evens) m_odd
, bench "alterF no rules delete present" $ whnf (atDelNoRules evens) m
, bench "alterF alter absent" $ whnf (atAlt id evens) m_odd
, bench "alterF alter insert" $ whnf (atAlt (const (Just 1)) evens) m_odd
, bench "alterF alter update" $ whnf (atAlt id evens) m_even
, bench "alterF alter delete" $ whnf (atAlt (const Nothing) evens) m
, bench "alterF no rules alter absent" $ whnf (atAltNoRules id evens) m_odd
, bench "alterF no rules alter insert" $ whnf (atAltNoRules (const (Just 1)) evens) m_odd
, bench "alterF no rules alter update" $ whnf (atAltNoRules id evens) m_even
, bench "alterF no rules alter delete" $ whnf (atAltNoRules (const Nothing) evens) m
, bench "mapMaybe" $ whnf (M.mapMaybe maybeDel) m
, bench "mapMaybeWithKey" $ whnf (M.mapMaybeWithKey (const maybeDel)) m
, bench "fromList" $ whnf M.fromList elems
Expand All @@ -43,7 +73,11 @@ main = do
]
where
elems = zip keys values
elems_even = zip evens evens
elems_odd = zip odds odds
keys = [1..2^12]
evens = [2,4..2^12]
odds = [1,3..2^12]
values = [1..2^12]
sum k v1 v2 = k + v1 + v2
consPair k v xs = (k, v) : xs
Expand Down Expand Up @@ -90,6 +124,48 @@ upd' xs m = foldl' (\m k -> snd $ M.updateLookupWithKey (\_ a -> Just a) k m) m
alt :: [Int] -> M.IntMap Int -> M.IntMap Int
alt xs m = foldl' (\m k -> M.alter id k m) m xs

newtype TestIdentity a = TestIdentity { runTestIdentity :: a }

instance Functor TestIdentity where
#if MIN_VERSION_base(4,8,0)
fmap = coerce
#else
fmap f (Ident a) = Ident (f a)
#endif

newtype TestConst a b = TestConst { getTestConst :: a }
instance Functor (TestConst a) where
fmap _ (TestConst a) = TestConst a

atLookup :: [Int] -> M.IntMap Int -> Int
atLookup xs m = foldl' (\n k -> fromMaybe n (getConst (M.alterF Const k m))) 0 xs

atLookupNoRules :: [Int] -> M.IntMap Int -> Int
atLookupNoRules xs m =
foldl' (\n k -> fromMaybe n (getTestConst (M.alterF TestConst k m))) 0 xs

atIns :: [(Int, Int)] -> M.IntMap Int -> M.IntMap Int
atIns xs m =
foldl' (\m (k, v) -> runIdentity (M.alterF (\_ -> Identity (Just v)) k m)) m xs

atInsNoRules :: [(Int, Int)] -> M.IntMap Int -> M.IntMap Int
atInsNoRules xs m =
foldl' (\m (k, v) -> runTestIdentity (M.alterF (\_ -> TestIdentity (Just v)) k m)) m xs

atDel :: [Int] -> M.IntMap Int -> M.IntMap Int
atDel xs m = foldl' (\m k -> runIdentity (M.alterF (\_ -> Identity Nothing) k m)) m xs

atDelNoRules :: [Int] -> M.IntMap Int -> M.IntMap Int
atDelNoRules xs m =
foldl' (\m k -> runTestIdentity (M.alterF (\_ -> TestIdentity Nothing) k m)) m xs

atAlt :: (Maybe Int -> Maybe Int) -> [Int] -> M.IntMap Int -> M.IntMap Int
atAlt f xs m = foldl' (\m k -> runIdentity (M.alterF (Identity . f) k m)) m xs

atAltNoRules :: (Maybe Int -> Maybe Int) -> [Int] -> M.IntMap Int -> M.IntMap Int
atAltNoRules f xs m =
foldl' (\m k -> runTestIdentity (M.alterF (TestIdentity . f) k m)) m xs

maybeDel :: Int -> Maybe Int
maybeDel n | n `mod` 3 == 0 = Nothing
| otherwise = Just n
77 changes: 77 additions & 0 deletions tests/intmap-properties.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import Data.IntMap.Lazy as Data.IntMap hiding (showTree)
import Data.IntMap.Internal.Debug (showTree)
import IntMapValidity (valid)

#if MIN_VERSION_base(4,9,0)
import Control.Applicative (Const(..))
#endif
#if MIN_VERSION_base(4,8,0)
import Data.Coerce (coerce)
import Data.Functor.Identity (Identity (..))
#endif
import Data.Monoid
import Data.Maybe hiding (mapMaybe)
import qualified Data.Maybe as Maybe (mapMaybe)
Expand Down Expand Up @@ -57,6 +64,7 @@ main = defaultMain
, testCase "updateWithKey" test_updateWithKey
, testCase "updateLookupWithKey" test_updateLookupWithKey
, testCase "alter" test_alter
, testCase "alterF" test_alterF
, testCase "union" test_union
, testCase "mappend" test_mappend
, testCase "unionWith" test_unionWith
Expand Down Expand Up @@ -146,6 +154,9 @@ main = defaultMain
, testProperty "toAscList+toDescList" prop_ascDescList
, testProperty "fromList" prop_fromList
, testProperty "alter" prop_alter
#if MIN_VERSION_base(4,8,0)
, testProperty "alterF_Identity" prop_alterF_IdentityRules
#endif
, testProperty "index" prop_index
, testProperty "index_lookup" prop_index_lookup
, testProperty "null" prop_null
Expand Down Expand Up @@ -402,9 +413,50 @@ test_alter = do
alter g 7 (fromList [(5,"a"), (3,"b")]) @?= fromList [(3, "b"), (5, "a"), (7, "c")]
alter g 5 (fromList [(5,"a"), (3,"b")]) @?= fromList [(3, "b"), (5, "c")]
where
f, g :: Maybe String -> Maybe String
f _ = Nothing
g _ = Just "c"

test_alterF :: Assertion
test_alterF = do
let m = fromList [(5,"a"), (3,"b")]
-- List applicative
alterF fList 7 m @?= [fromList [(3, "b"), (5, "a")]]
alterF fList 5 m @?= [singleton 3 "b"]
alterF gList 7 m @?= [fromList [(3, "b"), (5, "a"), (7, "c")]]
alterF gList 5 m @?= [fromList [(3, "b"), (5, "c")]]
#if MIN_VERSION_base(4,8,0)
-- Identity applicative
alterF fIdentity 7 m @?= Identity (fromList [(3, "b"), (5, "a")])
alterF fIdentity 5 m @?= Identity (singleton 3 "b")
alterF gIdentity 7 m @?= Identity (fromList [(3, "b"), (5, "a"), (7, "c")])
alterF gIdentity 5 m @?= Identity (fromList [(3, "b"), (5, "c")])
#endif
#if MIN_VERSION_base(4,9,0)
-- Const applicative
alterF fConst 7 m @?= Const False
alterF fConst 5 m @?= Const False
alterF gConst 7 m @?= Const True
alterF gConst 5 m @?= Const True
#endif
where
fList, gList :: Maybe String -> [Maybe String]
fList _ = [Nothing]
gList _ = [Just "c"]

#if MIN_VERSION_base(4,8,0)
fIdentity, gIdentity :: Maybe String -> Identity (Maybe String)
fIdentity _ = Identity Nothing
gIdentity _ = Identity (Just "c")
#endif

#if MIN_VERSION_base(4,9,0)
fConst, gConst :: Maybe String -> Const Bool (Maybe String)
fConst _ = Const False
gConst _ = Const True
#endif


----------------------------------------------------------------
-- Combine

Expand Down Expand Up @@ -963,6 +1015,31 @@ prop_alter t k = valid t' .&&. case lookup k t of
f Nothing = Just ()
f (Just ()) = Nothing

#if MIN_VERSION_base(4,8,0)
-- Verify that the rewrite rules for Identity give the same result as the
-- non-rewritten version. We use our own TestIdentity functor to compare
-- against.

newtype TestIdentity a = TestIdentity { runTestIdentity :: a }

instance Functor TestIdentity where
fmap = coerce

prop_alterF_IdentityRules :: UMap -> Int -> Property
prop_alterF_IdentityRules t k =
valid tIdentity .&&.
valid tTestIdentity .&&.
tIdentity == tTestIdentity
where
tIdentity = runIdentity $ alterF fIdentity k t
fIdentity Nothing = Identity (Just ())
fIdentity (Just ()) = Identity Nothing

tTestIdentity = runTestIdentity $ alterF fTest k t
fTest Nothing = TestIdentity (Just ())
fTest (Just ()) = TestIdentity (Nothing)
#endif

------------------------------------------------------------------------
-- Compare against the list model (after nub on keys)

Expand Down