on main help sign in
[#31] Add Semigroup and Monoid instances (#34)

* [#31] Add Semigroup and Monoid instances * Add imports * Fix imports again * Fix cache for stack * Fix review comments * Fix Travis cache properly

commitdccb742ca12c6ddf15434929ce229e5e954fd5e8 authorDmitrii Kovanikov <kovanikov@gmail.com> dateTue 10 Jul 2018, 13:45 (8 years ago) parentbc49de4
5 files changed+70−14
.travis.yml+1−1 CHANGELOG.md+6−4 src/Data/TypeRep/Map.hs+16−2 test/Test/TypeRep/MapProperty.hs+44−4 typerep-map.cabal+3−3
.travis.yml+1−1
@@ -9,7 +9,7 @@ cache:
99 - "$HOME/.cabal"
1010 - "$HOME/.ghc"
1111 - "$HOME/.stack"
12− - ".stack-work"
12+ - "$HOME/build/kowainik/typerep-map/.stack-work"
1313
1414matrix:
1515
CHANGELOG.md+6−4
@@ -1,10 +1,12 @@
11Change log
22==========
33
4−typerep-map uses [PVP Versioning][1].
4+`typerep-map` uses [PVP Versioning][1].
55The change log is available [on GitHub][2].
66
7−[1]: https://pvp.haskell.org
8−[2]: https://github.com/vrom911/typerep-map/releases
9−# 0.1.0.0
7+# 0.1.0
8+
109* Initially created.
10+
11+[1]: https://pvp.haskell.org
12+[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
src/Data/TypeRep/Map.hs+16−2
@@ -2,6 +2,7 @@
22{-# LANGUAGE BangPatterns #-}
33{-# LANGUAGE DataKinds #-}
44{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE InstanceSigs #-}
56{-# LANGUAGE KindSignatures #-}
67{-# LANGUAGE MagicHash #-}
78{-# LANGUAGE PolyKinds #-}
@@ -50,6 +51,7 @@ import Data.Maybe (fromJust)
5051import Data.Primitive.Array (Array, indexArray, mapArray')
5152import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
5253import Data.Proxy (Proxy (..))
54+import Data.Semigroup (Semigroup (..))
5355import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
5456import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
5557import GHC.Exts (inline, sortWith)
@@ -73,15 +75,27 @@ data TypeRepMap (f :: k -> Type) = TypeRepMap
7375instance Show (TypeRepMap f) where
7476 show = show . toFingerprints
7577
78+-- | Uses 'union' to combine 'TypeRepMap's.
79+instance Semigroup (TypeRepMap f) where
80+ (<>) :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
81+ (<>) = union
82+ {-# INLINE (<>) #-}
83+
84+instance Monoid (TypeRepMap f) where
85+ mempty = TypeRepMap mempty mempty mempty
86+ mappend = (<>)
87+ {-# INLINE mempty #-}
88+ {-# INLINE mappend #-}
89+
7690-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
7791toFingerprints :: TypeRepMap f -> [Fingerprint]
7892toFingerprints TypeRepMap{..} =
7993 zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
8094
81−
8295-- | Empty structure.
8396empty :: TypeRepMap f
84−empty = TypeRepMap mempty mempty mempty
97+empty = mempty
98+{-# INLINE empty #-}
8599
86100-- | Construct a 'TypeRepMap' with a single element.
87101one :: forall a f . Typeable a => f a -> TypeRepMap f
test/Test/TypeRep/MapProperty.hs+44−4
@@ -1,6 +1,7 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE KindSignatures #-}
3−{-# LANGUAGE PolyKinds #-}
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE PolyKinds #-}
45
56{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
67
@@ -9,22 +10,31 @@ module Test.TypeRep.MapProperty where
910import Prelude hiding (lookup)
1011
1112import Data.Proxy (Proxy (..))
13+import Data.Semigroup (Semigroup (..))
1214import GHC.Stack (HasCallStack)
1315import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
1416import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
1517import Test.Tasty (TestName, TestTree)
1618import Test.Tasty.Hedgehog (testProperty)
1719
18−import Data.TypeRep.Map (TF (..), TypeRepMap, delete, fromList, insert, lookup, member)
20+import Data.TypeRep.Map (TF (..), TypeRepMap (..), delete, fromList, insert, lookup, member)
1921
2022import qualified Hedgehog.Gen as Gen
2123import qualified Hedgehog.Range as Range
2224
25+----------------------------------------------------------------------------
26+-- Common utils
27+----------------------------------------------------------------------------
28+
2329type PropertyTest = [TestTree]
2430
2531prop :: HasCallStack => TestName -> PropertyT IO () -> PropertyTest
2632prop testName = pure . testProperty testName . property
2733
34+----------------------------------------------------------------------------
35+-- Map modification properties
36+----------------------------------------------------------------------------
37+
2838test_InsertLookup :: PropertyTest
2939test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
3040 m <- forAll genMap
@@ -50,6 +60,36 @@ test_DeleteMember = prop "member k . delete k == False" $ do
5060 else
5161 member @n (delete @n m) === False
5262
63+----------------------------------------------------------------------------
64+-- Semigroup and Monoid laws
65+----------------------------------------------------------------------------
66+
67+-- This newtype is used to compare 'TypeRepMap's using only 'Fingerprint's. It's
68+-- not a good idea to write such `Eq` instance for `TypeRepMap` itself because
69+-- it doesn't compare values so it's not true equality. But this should be
70+-- enough for tests.
71+newtype FpMap f = FpMap (TypeRepMap f)
72+ deriving (Show, Semigroup, Monoid)
73+
74+instance Eq (FpMap f) where
75+ FpMap (TypeRepMap as1 bs1 _) == FpMap (TypeRepMap as2 bs2 _) =
76+ as1 == as2 && bs1 == bs2
77+
78+test_SemigroupAssoc :: PropertyTest
79+test_SemigroupAssoc = prop "x <> (y <> z) == (x <> y) <> z" $ do
80+ x <- FpMap <$> forAll genMap
81+ y <- FpMap <$> forAll genMap
82+ z <- FpMap <$> forAll genMap
83+
84+ (x <> (y <> z)) === ((x <> y) <> z)
85+
86+test_MonoidIdentity :: PropertyTest
87+test_MonoidIdentity = prop "x <> mempty == mempty <> x == x" $ do
88+ x <- FpMap <$> forAll genMap
89+
90+ x <> mempty === x
91+ mempty <> x === x
92+
5393----------------------------------------------------------------------------
5494-- Generators
5595----------------------------------------------------------------------------
typerep-map.cabal+3−3
@@ -1,5 +1,5 @@
11name: typerep-map
2−version: 1.0
2+version: 0.1.0
33synopsis: Efficient implementation of data type with types as keys
44description: See README.md for more details.
55homepage: https://github.com/kowainik/typerep-map
@@ -26,10 +26,10 @@ library
2626 exposed-modules: Data.TypeRep.Map
2727 Data.TypeRep.TMap
2828 ghc-options: -Wall
29− build-depends: base
29+ build-depends: base >= 4.9 && < 5
3030 , containers
3131 , ghc-prim
32− , primitive
32+ , primitive >= 0.6.4
3333 default-extensions: OverloadedStrings
3434 RecordWildCards
3535 ScopedTypeVariables