on 1b739d8 help sign in
.github/workflows/ci.yml+4−3
since splitsnapshots
unifiedsplit
@@ -1,10 +1,11 @@
1name: CI
1name: CI
2
2
3−# Trigger the workflow on push or pull request, but only for the main branch
4on:
3on:
4+ # Trigger the workflow on push or pull request,
5+ # but only for the master branch
5 push:
6 push:
6 branches:
7 branches:
7− - main
8+ - master
8 pull_request:
9 pull_request:
9
10
10jobs:
11jobs:
@@ -23,7 +24,7 @@ jobs:
23
24
24 steps:
25 steps:
25 - uses: actions/checkout@v2
26 - uses: actions/checkout@v2
26− if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/main'
27+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'
27
28
28 - uses: actions/setup-haskell@v1
29 - uses: actions/setup-haskell@v1
29 name: Setup Haskell
30 name: Setup Haskell
CHANGELOG.md+1−1
@@ -53,4 +53,4 @@ The changelog is available [on GitHub][2].
53* Initially created.
53* Initially created.
54
54
55[1]: https://pvp.haskell.org
55[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
56+[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
README.md+1−1
@@ -4,7 +4,7 @@
4
4
5[![GitHub CI](https://github.com/kowainik/typerep-map/workflows/CI/badge.svg)](https://github.com/kowainik/typerep-map/actions)
5[![GitHub CI](https://github.com/kowainik/typerep-map/workflows/CI/badge.svg)](https://github.com/kowainik/typerep-map/actions)
6[![Build status](https://img.shields.io/travis/kowainik/typerep-map.svg?logo=travis)](https://travis-ci.org/kowainik/typerep-map)
6[![Build status](https://img.shields.io/travis/kowainik/typerep-map.svg?logo=travis)](https://travis-ci.org/kowainik/typerep-map)
7−[![Windows build status](https://ci.appveyor.com/api/projects/status/github/kowainik/typerep-map?branch=main&svg=true)](https://ci.appveyor.com/project/kowainik/typerep-map)
7+[![Windows build status](https://ci.appveyor.com/api/projects/status/github/kowainik/typerep-map?branch=master&svg=true)](https://ci.appveyor.com/project/kowainik/typerep-map)
8[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
8[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
9[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
9[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
10[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
10[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
src/Data/TMap.hs+1−33
@@ -37,8 +37,6 @@ module Data.TMap
37 , delete
37 , delete
38 , unionWith
38 , unionWith
39 , union
39 , union
40− , intersectionWith
41− , intersection
42 , map
40 , map
43 , adjust
41 , adjust
44 , alter
42 , alter
@@ -48,8 +46,6 @@ module Data.TMap
48 , member
46 , member
49 , size
47 , size
50 , keys
48 , keys
51− , keysWith
52− , toListWith
53 ) where
49 ) where
54
50
55import Prelude hiding (lookup, map)
51import Prelude hiding (lookup, map)
@@ -57,7 +53,7 @@ import Prelude hiding (lookup, map)
57import Data.Functor.Identity (Identity (..))
53import Data.Functor.Identity (Identity (..))
58import Data.Typeable (Typeable)
54import Data.Typeable (Typeable)
59import GHC.Exts (coerce)
55import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
56+import Type.Reflection (SomeTypeRep)
61
57
62import qualified Data.TypeRepMap as F
58import qualified Data.TypeRepMap as F
63
59
@@ -136,24 +132,6 @@ union :: TMap -> TMap -> TMap
136union = F.union
132union = F.union
137{-# INLINE union #-}
133{-# INLINE union #-}
138
134
139−-- | The intersection of two 'TMap's using a combining function.
140−--
141−-- @O(n + m)@
142−intersectionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
143−intersectionWith f = F.intersectionWith fId
144− where
145− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
146− fId y1 y2 = f (coerce y1) (coerce y2)
147−{-# INLINE intersectionWith #-}
148−
149−-- | The intersection of two 'TMap's.
150−-- It keeps all values from the first map whose keys are present in the second.
151−--
152−-- @O(n + m)@
153−intersection :: TMap -> TMap -> TMap
154−intersection = F.intersection
155−{-# INLINE intersection #-}
156−
157{- | Lookup a value of the given type in a 'TMap'.
135{- | Lookup a value of the given type in a 'TMap'.
158
136
159>>> x = lookup $ insert (11 :: Int) empty
137>>> x = lookup $ insert (11 :: Int) empty
@@ -187,16 +165,6 @@ keys :: TMap -> [SomeTypeRep]
187keys = F.keys
165keys = F.keys
188{-# INLINE keys #-}
166{-# INLINE keys #-}
189
167
190−-- | Return the list of keys by wrapping them with a user-provided function.
191−keysWith :: (forall a. TypeRep a -> r) -> TMap -> [r]
192−keysWith = F.keysWith
193−{-# INLINE keysWith #-}
194−
195−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
196−toListWith :: (forall a. Typeable a => a -> r) -> TMap -> [r]
197−toListWith = F.toListWith
198−{-# INLINE toListWith #-}
199−
200-- | Map a function over the values.
168-- | Map a function over the values.
201map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
169map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202map f = F.hoistWithKey (liftToIdentity f)
170map f = F.hoistWithKey (liftToIdentity f)
src/Data/TypeRepMap.hs+1−5
@@ -5,7 +5,7 @@ Copyright: (c) 2017-2020 Kowainik
5SPDX-License-Identifier: MPL-2.0
5SPDX-License-Identifier: MPL-2.0
6Maintainer: Kowainik <xrom.xkov@gmail.com>
6Maintainer: Kowainik <xrom.xkov@gmail.com>
7
7
8−A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
8+ A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
9sort of parametrization may be familiar to users of @vinyl@ records.
9sort of parametrization may be familiar to users of @vinyl@ records.
10
10
11@'TypeRepMap' f@ is a more efficient replacement for @DMap
11@'TypeRepMap' f@ is a more efficient replacement for @DMap
@@ -63,16 +63,12 @@ module Data.TypeRepMap
63 , hoistWithKey
63 , hoistWithKey
64 , unionWith
64 , unionWith
65 , union
65 , union
66− , intersectionWith
67− , intersection
68
66
69 -- * Query
67 -- * Query
70 , lookup
68 , lookup
71 , member
69 , member
72 , size
70 , size
73 , keys
71 , keys
74− , keysWith
75− , toListWith
76
72
77 -- * 'IsList'
73 -- * 'IsList'
78 , WrapTypeable (..)
74 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+8−59
@@ -205,8 +205,6 @@ insert x m
205-- inferred type variables (which are not subject to type applications).
205-- inferred type variables (which are not subject to type applications).
206type KindOf (a :: k) = k
206type KindOf (a :: k) = k
207
207
208−type ArgKindOf (f :: k -> l) = k
209−
210{- | Delete a value from a 'TypeRepMap'.
208{- | Delete a value from a 'TypeRepMap'.
211
209
212TypeRepMap optimizes for fast reads rather than modifications, as a trade-off deletes are
210TypeRepMap optimizes for fast reads rather than modifications, as a trade-off deletes are
@@ -352,48 +350,6 @@ union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352union = unionWith const
350union = unionWith const
353{-# INLINE union #-}
351{-# INLINE union #-}
354
352
355−-- | The 'intersection' of two 'TypeRepMap's using a combining function
356−--
357−-- @O(n + m)@
358−intersectionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
359−intersectionWith f ma mb =
360− fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
361− where
362− f' :: forall x. TypeRep x -> f x -> f x -> f x
363− f' tr = withTypeable tr f
364−
365− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
366− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
367−
368− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
369− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
370− -- If either list is empty, the intersection must be finished.
371− mergeMaps _ [] = []
372− mergeMaps [] _ = []
373− -- Merge the two maps considering one element at a time.
374− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
375− case compare af bf of
376− -- Fingerprints are equal, union the elements using our function
377− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
378− -- fingerprints
379− EQ -> combine a b : mergeMaps as bs
380− -- First fingerprint must not be in the second map or we would have seen it by now
381− -- Skip it an move on
382− LT -> mergeMaps as (b : bs)
383− -- Second fingerprint must not be in the first map or we would have seen it by now
384− -- Skip it an move on
385− GT -> mergeMaps (a:as) bs
386−{-# INLINE intersectionWith #-}
387−
388−-- | The intersection of two 'TypeRepMap's.
389−-- It keeps all values from the first map whose keys are present in the second.
390−--
391−-- @O(n + m)@
392−intersection :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
393−intersection = intersectionWith const
394−{-# INLINE intersection #-}
395−
396−
397{- | Check if a value of the given type is present in a 'TypeRepMap'.
353{- | Check if a value of the given type is present in a 'TypeRepMap'.
398
354
399>>> member @Char $ one (Identity 'a')
355>>> member @Char $ one (Identity 'a')
@@ -429,22 +385,9 @@ size = sizeofPrimArray . fingerprintAs
429
385
430-- | Return the list of 'SomeTypeRep' from the keys.
386-- | Return the list of 'SomeTypeRep' from the keys.
431keys :: TypeRepMap f -> [SomeTypeRep]
387keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
388+keys TypeRepMap{..} = SomeTypeRep . anyToTypeRep <$> toList trKeys
433{-# INLINE keys #-}
389{-# INLINE keys #-}
434
390
435−-- | Return the list of keys by wrapping them with a user-provided function.
436−keysWith :: (forall (a :: ArgKindOf f). TypeRep a -> r) -> TypeRepMap f -> [r]
437−keysWith f TypeRepMap{..} = f . anyToTypeRep <$> toList trKeys
438−{-# INLINE keysWith #-}
439−
440−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
441−toListWith :: forall f r. (forall (a :: ArgKindOf f). Typeable a => f a -> r) -> TypeRepMap f -> [r]
442−toListWith f = map toF . toTriples
443− where
444− withTypeRep :: TypeRep a -> f a -> r
445− withTypeRep tr an = withTypeable tr $ f an
446− toF (_, an, k) = withTypeRep (unsafeCoerce k) (fromAny an)
447−
448-- | Binary searched based on this article
391-- | Binary searched based on this article
449-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
392-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450-- with modification for our two-vector search case.
393-- with modification for our two-vector search case.
@@ -521,6 +464,9 @@ data WrapTypeable f where
521instance Show (WrapTypeable f) where
464instance Show (WrapTypeable f) where
522 show (WrapTypeable (_ :: f a)) = show $ calcFp @a
465 show (WrapTypeable (_ :: f a)) = show $ calcFp @a
523
466
467+wrapTypeable :: TypeRep a -> f a -> WrapTypeable f
468+wrapTypeable tr = withTypeable tr WrapTypeable
469+
524{- |
470{- |
525
471
526prop> fromList . toList == 'id'
472prop> fromList . toList == 'id'
@@ -548,7 +494,10 @@ instance IsList (TypeRepMap f) where
548 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
494 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549
495
550 toList :: TypeRepMap f -> [WrapTypeable f]
496 toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
497+ toList = map toWrapTypeable . toTriples
498+ where
499+ toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
500+ toWrapTypeable (_, an, k) = wrapTypeable (unsafeCoerce k) (fromAny an)
552
501
553calcFp :: forall a . Typeable a => Fingerprint
502calcFp :: forall a . Typeable a => Fingerprint
554calcFp = typeRepFingerprint $ typeRep @a
503calcFp = typeRepFingerprint $ typeRep @a
test/Test/TypeRep/TypeRepMap.hs+4−62
@@ -5,18 +5,14 @@ module Test.TypeRep.TypeRepMap
5import Prelude hiding (lookup)
5import Prelude hiding (lookup)
6
6
7import Data.Functor.Identity (Identity (..))
7import Data.Functor.Identity (Identity (..))
8−import Data.List (sortBy)
9−import Data.Ord (comparing)
10−import Data.Typeable (cast)
11−import GHC.Exts (fromList, toList)
8+import GHC.Exts (fromList)
12−import Type.Reflection (SomeTypeRep(..), typeRep)
13−import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList, shouldSatisfy, expectationFailure)
9+import Test.Hspec (Spec, describe, it, shouldBe)
14
10
15−import Data.TMap (TMap, empty, insert, lookup, one, size, union, keys)
11+import Data.TMap (TMap, empty, insert, lookup, one, size, union)
16import Data.TypeRepMap.Internal (WrapTypeable (..))
12import Data.TypeRepMap.Internal (WrapTypeable (..))
17
13
18
14
19−-- Simple test for 'lookup', 'insert', 'size', 'keys', 'toList' functions.
15+-- Simple test for 'lookup', 'insert' and 'size' functions.
20typeRepMapSpec :: Spec
16typeRepMapSpec :: Spec
21typeRepMapSpec = describe "TypeRepMap" $ do
17typeRepMapSpec = describe "TypeRepMap" $ do
22 describe "Lookup Test" $ do
18 describe "Lookup Test" $ do
@@ -43,60 +39,6 @@ typeRepMapSpec = describe "TypeRepMap" $ do
43 lookup m `shouldBe` Just True
39 lookup m `shouldBe` Just True
44 lookup @Int m `shouldBe` Nothing
40 lookup @Int m `shouldBe` Nothing
45
41
46− describe "Keys Test" $ do
47− it "returns nothing on empty map" $
48− keys empty `shouldBe` []
49− it "returns the correct TypeRep" $
50− keys (one 'a') `shouldBe` [SomeTypeRep $ typeRep @Char]
51− it "returns the correct TypeReps for 10 different types" $
52− keys mapOf10 `shouldMatchList`
53− [ SomeTypeRep $ typeRep @Bool
54− , SomeTypeRep $ typeRep @[Bool]
55− , SomeTypeRep $ typeRep @(Maybe Bool)
56− , SomeTypeRep $ typeRep @(Maybe ())
57− , SomeTypeRep $ typeRep @[()]
58− , SomeTypeRep $ typeRep @()
59− , SomeTypeRep $ typeRep @String
60− , SomeTypeRep $ typeRep @(Maybe Char)
61− , SomeTypeRep $ typeRep @Char
62− , SomeTypeRep $ typeRep @Int
63− ]
64−
65− describe "ToList Test" $ do
66− it "returns nothing on empty map" $
67− toList empty `shouldSatisfy` null
68− it "returns correct result when 1 element is inserted" $
69− case toList (one 'a') of
70− [WrapTypeable (Identity x)] -> cast x `shouldBe` Just 'a'
71− _ -> expectationFailure "did not return exactly 1 result"
72− it "returns correct result when 10 elements are inserted" $ do
73− let
74− getTypeRep (WrapTypeable (Identity (_ :: a))) = SomeTypeRep $ typeRep @a
75− got = sortBy (comparing getTypeRep) (toList mapOf10)
76− expected = sortBy (comparing fst)
77− [ (SomeTypeRep $ typeRep @Bool,
78− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just True)
79− , (SomeTypeRep $ typeRep @[Bool],
80− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [True, False])
81− , (SomeTypeRep $ typeRep @(Maybe Bool),
82− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just True))
83− , (SomeTypeRep $ typeRep @(Maybe ()),
84− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just ()))
85− , (SomeTypeRep $ typeRep @[()],
86− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [()])
87− , (SomeTypeRep $ typeRep @(),
88− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ())
89− , (SomeTypeRep $ typeRep @String,
90− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ("aaa" :: String))
91− , (SomeTypeRep $ typeRep @(Maybe Char),
92− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just 'a'))
93− , (SomeTypeRep $ typeRep @Char,
94− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just 'a')
95− , (SomeTypeRep $ typeRep @Int,
96− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (11 :: Int))
97− ]
98− length got `shouldBe` 10
99− sequence_ $ zipWith snd expected got
100
42
101mapOf10 :: TMap
43mapOf10 :: TMap
102mapOf10 = insert True
44mapOf10 = insert True
test/Test/TypeRep/TypeRepMapProperty.hs+2−9
@@ -20,8 +20,8 @@ import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
20import Test.Hspec.Hedgehog (hedgehog)
20import Test.Hspec.Hedgehog (hedgehog)
21
21
22import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
22import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
23− lookup, member, union, generateOrderMapping, fromSortedList,
23+ lookup, member, generateOrderMapping, fromSortedList, alter,
24− adjust, alter, intersection)
24+ adjust)
25
25
26import qualified Hedgehog.Gen as Gen
26import qualified Hedgehog.Gen as Gen
27import qualified Hedgehog.Range as Range
27import qualified Hedgehog.Range as Range
@@ -39,7 +39,6 @@ typeRepMapPropertySpec = describe "TypeRepMap Property tests" $ do
39 alterDeleteSpec
39 alterDeleteSpec
40 alterAdjustSpec
40 alterAdjustSpec
41 alterModifySpec
41 alterModifySpec
42− intersectionSpec
43 describe "Internal helpers" $ do
42 describe "Internal helpers" $ do
44 generateOrderMappingInvariantSpec
43 generateOrderMappingInvariantSpec
45 describe "Instance Laws" $ do
44 describe "Instance Laws" $ do
@@ -119,12 +118,6 @@ alterModifySpec = it "lookup k (alter f) == f (lookup k m)" $ hedgehog $ do
119 | otherwise = Just $ IntProxy p (n * 10)
118 | otherwise = Just $ IntProxy p (n * 10)
120 lookup @n @IntProxy (alter @n f m) === f (lookup @n @IntProxy m)
119 lookup @n @IntProxy (alter @n f m) === f (lookup @n @IntProxy m)
121
120
122−intersectionSpec :: Property
123−intersectionSpec = it "m `intersection` (m `union` n) == m" $ hedgehog $ do
124− m <- forAll genMap
125− n <- forAll genMap
126− m `intersection` (m `union` n) === m
127−
128----------------------------------------------------------------------------
121----------------------------------------------------------------------------
129-- Internal helpers
122-- Internal helpers
130----------------------------------------------------------------------------
123----------------------------------------------------------------------------