git.iko.soy/monoidal-containers
on master help sign in
Merge branch 'master' of github.com:bgamari/monoidal-containers
aa2316dBen Gamari
.gitignorenew file+2
@@ -0,0 +1,2 @@
1+dist
2+cabal.config
README.mdnew file+8
@@ -0,0 +1,8 @@
1+# monoidal-containers
2+
3+Often when working with the containers offered by the `containers` and
4+`unordered-containers` packages one would prefer the monoidal structure
5+of the values to be used when resolving conflicts between keys when merging
6+structures. Sadly, these are not the semantics offered by the provided
7+instances. This package provides `newtypes` with an appropriate set of
8+instances and utility functions to make them usable.
src/Data/HashMap/Monoidal.hs+45−4
@@ -23,15 +23,20 @@ module Data.HashMap.Monoidal
23 , member
23 , member
24 , notMember
24 , notMember
25 , lookup
25 , lookup
26+ , lookupM
26 , elems
27 , elems
27 , keys
28 , keys
29+ , delete
30+ , mapKeys
31+ , modify
32+ , modifyDef
28 ) where
33 ) where
29
34
30import Prelude hiding (lookup)
35import Prelude hiding (lookup)
36+import Data.Maybe (fromMaybe)
31import Data.Monoid
37import Data.Monoid
32import Data.Foldable (Foldable)
38import Data.Foldable (Foldable)
33−import Data.Traversable (Traversable)
34−import Control.Applicative (Applicative, pure)
39+import Control.Applicative (pure)
35import Data.Data (Data)
40import Data.Data (Data)
36import Data.Typeable (Typeable)
41import Data.Typeable (Typeable)
37
42
@@ -96,9 +101,9 @@ instance Newtype (MonoidalHashMap k a) (M.HashMap k a) where
96 {-# INLINE unpack #-}
101 {-# INLINE unpack #-}
97
102
98#if MIN_VERSION_base(4,7,0)
103#if MIN_VERSION_base(4,7,0)
99−instance (Eq k, Hashable k) => IsList (MonoidalHashMap k a) where
104+instance (Eq k, Hashable k, Monoid a) => IsList (MonoidalHashMap k a) where
100 type Item (MonoidalHashMap k a) = (k, a)
105 type Item (MonoidalHashMap k a) = (k, a)
101− fromList = MM . M.fromList
106+ fromList = MM . M.fromListWith mappend
102 {-# INLINE fromList #-}
107 {-# INLINE fromList #-}
103 toList = M.toList . unpack
108 toList = M.toList . unpack
104 {-# INLINE toList #-}
109 {-# INLINE toList #-}
@@ -130,6 +135,12 @@ lookup :: (Eq k, Hashable k) => k -> MonoidalHashMap k v -> Maybe v
130lookup k = M.lookup k . unpack
135lookup k = M.lookup k . unpack
131{-# INLINE lookup #-}
136{-# INLINE lookup #-}
132
137
138+-- | /O(log n)/ Return the value to which the specified key is mapped,
139+-- or mempty if this map contains no mapping for the key.
140+lookupM :: (Eq k, Hashable k, Monoid v) => k -> MonoidalHashMap k v -> v
141+lookupM k = fromMaybe mempty . M.lookup k . unpack
142+{-# INLINE lookupM #-}
143+
133-- | /O(log n)/. Delete a key and its value from the map. When the key is not
144-- | /O(log n)/. Delete a key and its value from the map. When the key is not
134-- a member of the map, the original map is returned.
145-- a member of the map, the original map is returned.
135delete :: (Eq k, Hashable k) => k -> MonoidalHashMap k a -> MonoidalHashMap k a
146delete :: (Eq k, Hashable k) => k -> MonoidalHashMap k a -> MonoidalHashMap k a
@@ -148,3 +159,33 @@ elems = M.elems . unpack
148keys :: MonoidalHashMap k a -> [k]
159keys :: MonoidalHashMap k a -> [k]
149keys = M.keys . unpack
160keys = M.keys . unpack
150{-# INLINE keys #-}
161{-# INLINE keys #-}
162+
163+-- | /O(log n)/. Modify a value on some key with a function, if value
164+-- under key doesn't exist -- use mempty.
165+modify :: (Monoid a, Hashable k, Eq k)
166+ => (a -> a)
167+ -> k -> MonoidalHashMap k a
168+ -> MonoidalHashMap k a
169+modify f k = pack
170+ . M.insertWith (\_ old -> f old) k (f mempty)
171+ . unpack
172+{-# INLINE modify #-}
173+
174+-- | /O(log n)/. Modify a value on some key with a function, providing
175+-- a default value if that key doesn't exist.
176+modifyDef :: (Monoid a, Hashable k, Eq k)
177+ => a -> (a -> a)
178+ -> k -> MonoidalHashMap k a
179+ -> MonoidalHashMap k a
180+modifyDef d f k = pack
181+ . M.insertWith (\_ old -> f old) k d
182+ . unpack
183+{-# INLINE modifyDef #-}
184+
185+-- | /O(n)/. Map a function to each key of a map
186+mapKeys :: (Monoid a, Hashable k, Eq k, Hashable k', Eq k')
187+ => (k -> k') -> MonoidalHashMap k a
188+ -> MonoidalHashMap k' a
189+mapKeys f = fromList
190+ . map (\(k, v) -> (f k, v))
191+ . toList