git.iko.soy/monoidal-containers
on master help sign in
Make mapKeys correspond semantically
commitc87018b6552309ed3d337e53f5a97ad3d21a893d author3noch <eacameron@gmail.com> dateTue 2 Apr 2019, 21:13 (8 years ago) parent07dde25
2 files changed+40−4
src/Data/Map/Monoidal.hs+20−2 src/Data/Map/Monoidal/Strict.hs+20−2
src/Data/Map/Monoidal.hs+20−2
@@ -423,14 +423,32 @@ mapAccumRWithKey :: forall k a b c. (a -> k -> b -> (a, c)) -> a -> MonoidalMap
423423mapAccumRWithKey = coerce (M.mapAccumRWithKey :: (a -> k -> b -> (a, c)) -> a -> M.Map k b -> (a, M.Map k c))
424424{-# INLINE mapAccumRWithKey #-}
425425
426−mapKeys :: forall k1 k2 a. Ord k2 => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
427−mapKeys = coerce (M.mapKeys :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
426+mapKeys :: forall k1 k2 a. (Ord k2, Semigroup a) => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
427+mapKeys = mapKeysWith (<>)
428428{-# INLINE mapKeys #-}
429429
430430mapKeysWith :: forall k1 k2 a. Ord k2 => (a -> a -> a) -> (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
431431mapKeysWith = coerce (M.mapKeysWith :: (a -> a -> a) -> (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
432432{-# INLINE mapKeysWith #-}
433433
434+-- | /O(n)/.
435+-- @'mapKeysMonotonic' f s == 'mapKeys' f s@, but works only when @f@
436+-- is strictly increasing (both monotonic and injective).
437+-- That is, for any values @x@ and @y@, if @x@ < @y@ then @f x@ < @f y@
438+-- and @f@ is injective (i.e. it never maps two input keys to the same output key).
439+-- /The precondition is not checked./
440+-- Semi-formally, we have:
441+--
442+-- > and [x < y ==> f x < f y | x <- ls, y <- ls]
443+-- > ==> mapKeysMonotonic f s == mapKeys f s
444+-- > where ls = keys s
445+--
446+-- This means that @f@ maps distinct original keys to distinct resulting keys.
447+-- This function has better performance than 'mapKeys'.
448+--
449+-- > mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")]) == fromList [(6, "b"), (10, "a")]
450+-- > valid (mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")])) == True
451+-- > valid (mapKeysMonotonic (\ _ -> 1) (fromList [(5,"a"), (3,"b")])) == False
434452mapKeysMonotonic :: forall k1 k2 a. (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
435453mapKeysMonotonic = coerce (M.mapKeysMonotonic :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
436454{-# INLINE mapKeysMonotonic #-}
src/Data/Map/Monoidal/Strict.hs+20−2
@@ -422,14 +422,32 @@ mapAccumRWithKey :: forall k a b c. (a -> k -> b -> (a, c)) -> a -> MonoidalMap
422422mapAccumRWithKey = coerce (M.mapAccumRWithKey :: (a -> k -> b -> (a, c)) -> a -> M.Map k b -> (a, M.Map k c))
423423{-# INLINE mapAccumRWithKey #-}
424424
425−mapKeys :: forall k1 k2 a. Ord k2 => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
426−mapKeys = coerce (M.mapKeys :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
425+mapKeys :: forall k1 k2 a. (Ord k2, Semigroup a) => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
426+mapKeys = mapKeysWith (<>)
427427{-# INLINE mapKeys #-}
428428
429429mapKeysWith :: forall k1 k2 a. Ord k2 => (a -> a -> a) -> (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
430430mapKeysWith = coerce (M.mapKeysWith :: (a -> a -> a) -> (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
431431{-# INLINE mapKeysWith #-}
432432
433+-- | /O(n)/.
434+-- @'mapKeysMonotonic' f s == 'mapKeys' f s@, but works only when @f@
435+-- is strictly increasing (both monotonic and injective).
436+-- That is, for any values @x@ and @y@, if @x@ < @y@ then @f x@ < @f y@
437+-- and @f@ is injective (i.e. it never maps two input keys to the same output key).
438+-- /The precondition is not checked./
439+-- Semi-formally, we have:
440+--
441+-- > and [x < y ==> f x < f y | x <- ls, y <- ls]
442+-- > ==> mapKeysMonotonic f s == mapKeys f s
443+-- > where ls = keys s
444+--
445+-- This means that @f@ maps distinct original keys to distinct resulting keys.
446+-- This function has better performance than 'mapKeys'.
447+--
448+-- > mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")]) == fromList [(6, "b"), (10, "a")]
449+-- > valid (mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")])) == True
450+-- > valid (mapKeysMonotonic (\ _ -> 1) (fromList [(5,"a"), (3,"b")])) == False
433451mapKeysMonotonic :: forall k1 k2 a. (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
434452mapKeysMonotonic = coerce (M.mapKeysMonotonic :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
435453{-# INLINE mapKeysMonotonic #-}