containers-0.8.1: Assorted concrete container types
Copyright(c) wren romano 2016
LicenseBSD-style
Maintainerlibraries@haskell.org
Portabilityportable
Safe HaskellSafe
LanguageHaskell2010

Data.IntMap.Merge.Lazy

Description

This module defines an API for writing functions that merge two maps. The key functions are merge and mergeA. Each of these can be used with several different "merge tactics".

The merge and mergeA functions are shared by the lazy and strict modules. Only the choice of merge tactics determines strictness. If you use mapMissing from Data.Map.Merge.Strict then the results will be forced before they are inserted. If you use mapMissing from this module then they will not.

Efficiency note

The Category, Applicative, and Monad instances for WhenMissing tactics are included because they are valid. However, they are inefficient in many cases and should usually be avoided. The instances for WhenMatched tactics should not pose any major efficiency problems.

Since: 0.5.9

Synopsis

Simple merge tactic types

type SimpleWhenMissing = WhenMissing Identity Source #

A tactic for dealing with keys present in one map but not the other in merge.

A tactic of type SimpleWhenMissing x z is an abstract representation of a function of type Key -> x -> Maybe z.

Since: 0.5.9

type SimpleWhenMatched = WhenMatched Identity Source #

A tactic for dealing with keys present in both maps in merge.

A tactic of type SimpleWhenMatched x y z is an abstract representation of a function of type Key -> x -> y -> Maybe z.

Since: 0.5.9

General combining function

merge Source #

Arguments

:: SimpleWhenMissing a c

What to do with keys in m1 but not m2

-> SimpleWhenMissing b c

What to do with keys in m2 but not m1

-> SimpleWhenMatched a b c

What to do with keys in both m1 and m2

-> IntMap a

Map m1

-> IntMap b

Map m2

-> IntMap c 

Merge two maps.

merge takes two SimpleWhenMissing tactics, a SimpleWhenMatched tactic and two maps. It uses the tactics to merge the maps. Its behavior is best understood via its fundamental tactics, mapMaybeMissing and zipWithMaybeMatched.

Consider

merge (mapMaybeMissing g1) (mapMaybeMissing g2) (zipWithMaybeMatched f) m1 m2
g1 k x = if k == 2 then Just ("1" ++ x) else Nothing
g2 k x = if k == 3 then Just ("2" ++ x) else Nothing
f k x y = if k == 6 then Just ("3" ++ x ++ y) else Nothing
m1 = fromList [(2,"a"), (4,"b"), (6,"c"), (8,"d"), (10,"e"), (12,"f")]
m2 = fromList [(3,"g"), (6,"h"), (9,"i"), (12,"j")]

merge will pass the keys and values to g1, g2, or f as appropriate, producing a Maybe for each element.

m1:      [ (2, "a"),            (4, "b"),    (6, "c"), (8, "d"),           (10, "e"),    (12, "f")]
m2:      [            (3, "g"),              (6, "h"),           (9, "i"),               (12, "j")]
result:  [ g1 2 "a",  g2 3 "g", g1 4 "b", f 6 "c" "h", g1 8 "d", g2 9 "i", g1 10 "e", f 12 "f" "j"]
       = [Just "1a", Just "2g",  Nothing,  Just "3ch",  Nothing,  Nothing,   Nothing,      Nothing]

The result map contains the Just values.

>>> merge (mapMaybeMissing g1) (mapMaybeMissing g2) (zipWithMaybeMatched f) m1 m2
fromList [(2,"1a"), (3,"2g"), (6,"3ch")]

The other tactics below are optimizations or simplifications of mapMaybeMissing for special cases. Most importantly,

When merge is given three arguments, it is inlined at the call site. To prevent excessive inlining, you should typically use merge to define your custom combining functions.

Examples:

unionWithKey f = merge preserveMissing preserveMissing (zipWithMatched f)
intersectionWithKey f = merge dropMissing dropMissing (zipWithMatched f)
differenceWith f = merge diffPreserve diffDrop f
symmetricDifference = merge diffPreserve diffPreserve (\ _ _ _ -> Nothing)
mapEachPiece f g h = merge (diffMapWithKey f) (diffMapWithKey g)

Since: 0.5.9

WhenMatched tactics

dropMatched :: forall (f :: Type -> Type) x y z. Applicative f => WhenMatched f x y z Source #

When a key is found in both maps, drop the key and values.

Since: 0.8.1

zipWithMaybeMatched :: forall (f :: Type -> Type) x y z. Applicative f => (Key -> x -> y -> Maybe z) -> WhenMatched f x y z Source #

When a key is found in both maps, apply a function to the key and values and maybe use the result in the merged map.

zipWithMaybeMatched
  :: (Key -> x -> y -> Maybe z)
  -> SimpleWhenMatched x y z

Since: 0.5.9

zipWithMatched :: forall (f :: Type -> Type) x y z. Applicative f => (Key -> x -> y -> z) -> WhenMatched f x y z Source #

When a key is found in both maps, apply a function to the key and values and use the result in the merged map.

zipWithMatched
  :: (Key -> x -> y -> z)
  -> SimpleWhenMatched x y z

Since: 0.5.9

WhenMissing tactics

mapMaybeMissing :: forall (f :: Type -> Type) x y. Applicative f => (Key -> x -> Maybe y) -> WhenMissing f x y Source #

Map over the entries whose keys are missing from the other map, optionally removing some. This is the most powerful SimpleWhenMissing tactic, but others are usually more efficient.

mapMaybeMissing :: (Key -> x -> Maybe y) -> SimpleWhenMissing x y
mapMaybeMissing f = traverseMaybeMissing (\k x -> pure (f k x))

but mapMaybeMissing uses fewer unnecessary Applicative operations.

Since: 0.5.9

dropMissing :: forall (f :: Type -> Type) x y. Applicative f => WhenMissing f x y Source #

Drop all the entries whose keys are missing from the other map.

dropMissing :: SimpleWhenMissing x y
dropMissing = mapMaybeMissing (\_ _ -> Nothing)

but dropMissing is much faster.

Since: 0.5.9

preserveMissing :: forall (f :: Type -> Type) x. Applicative f => WhenMissing f x x Source #

Preserve, unchanged, the entries whose keys are missing from the other map.

preserveMissing :: SimpleWhenMissing x x
preserveMissing = Merge.Lazy.mapMaybeMissing (\_ x -> Just x)

but preserveMissing is much faster.

Since: 0.5.9

mapMissing :: forall (f :: Type -> Type) x y. Applicative f => (Key -> x -> y) -> WhenMissing f x y Source #

Map over the entries whose keys are missing from the other map.

mapMissing :: (k -> x -> y) -> SimpleWhenMissing x y
mapMissing f = mapMaybeMissing (\k x -> Just $ f k x)

but mapMissing is somewhat faster.

Since: 0.5.9

filterMissing :: forall (f :: Type -> Type) x. Applicative f => (Key -> x -> Bool) -> WhenMissing f x x Source #

Filter the entries whose keys are missing from the other map.

filterMissing :: (k -> x -> Bool) -> SimpleWhenMissing x x
filterMissing f = Merge.Lazy.mapMaybeMissing $ \k x -> guard (f k x) *> Just x

but this should be a little faster.

Since: 0.5.9

Applicative merge tactic types

data WhenMissing (f :: Type -> Type) x y Source #

A tactic for dealing with keys present in one map but not the other in merge or mergeA.

A tactic of type WhenMissing f k x z is an abstract representation of a function of type Key -> x -> f (Maybe z).

Since: 0.5.9

Instances

Instances details
Monad f => Category (WhenMissing f :: Type -> Type -> Type) Source #

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

id :: WhenMissing f a a #

(.) :: WhenMissing f b c -> WhenMissing f a b -> WhenMissing f a c #

Monad f => Applicative (WhenMissing f x) Source #

Equivalent to ReaderT k (ReaderT x (MaybeT f)).

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

pure :: a -> WhenMissing f x a #

(<*>) :: WhenMissing f x (a -> b) -> WhenMissing f x a -> WhenMissing f x b #

liftA2 :: (a -> b -> c) -> WhenMissing f x a -> WhenMissing f x b -> WhenMissing f x c #

(*>) :: WhenMissing f x a -> WhenMissing f x b -> WhenMissing f x b #

(<*) :: WhenMissing f x a -> WhenMissing f x b -> WhenMissing f x a #

Monad f => Functor (WhenMissing f x) Source #

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

fmap :: (a -> b) -> WhenMissing f x a -> WhenMissing f x b #

(<$) :: a -> WhenMissing f x b -> WhenMissing f x a #

Monad f => Monad (WhenMissing f x) Source #

Equivalent to ReaderT k (ReaderT x (MaybeT f)).

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

(>>=) :: WhenMissing f x a -> (a -> WhenMissing f x b) -> WhenMissing f x b #

(>>) :: WhenMissing f x a -> WhenMissing f x b -> WhenMissing f x b #

return :: a -> WhenMissing f x a #

data WhenMatched (f :: Type -> Type) x y z Source #

A tactic for dealing with keys present in both maps in merge or mergeA.

A tactic of type WhenMatched f x y z is an abstract representation of a function of type Key -> x -> y -> f (Maybe z).

Since: 0.5.9

Instances

Instances details
Monad f => Category (WhenMatched f x :: Type -> Type -> Type) Source #

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

id :: WhenMatched f x a a #

(.) :: WhenMatched f x b c -> WhenMatched f x a b -> WhenMatched f x a c #

Monad f => Applicative (WhenMatched f x y) Source #

Equivalent to ReaderT Key (ReaderT x (ReaderT y (MaybeT f)))

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

pure :: a -> WhenMatched f x y a #

(<*>) :: WhenMatched f x y (a -> b) -> WhenMatched f x y a -> WhenMatched f x y b #

liftA2 :: (a -> b -> c) -> WhenMatched f x y a -> WhenMatched f x y b -> WhenMatched f x y c #

(*>) :: WhenMatched f x y a -> WhenMatched f x y b -> WhenMatched f x y b #

(<*) :: WhenMatched f x y a -> WhenMatched f x y b -> WhenMatched f x y a #

Functor f => Functor (WhenMatched f x y) Source #

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

fmap :: (a -> b) -> WhenMatched f x y a -> WhenMatched f x y b #

(<$) :: a -> WhenMatched f x y b -> WhenMatched f x y a #

Monad f => Monad (WhenMatched f x y) Source #

Equivalent to ReaderT Key (ReaderT x (ReaderT y (MaybeT f)))

Since: 0.5.9

Instance details

Defined in Data.IntMap.Internal

Methods

(>>=) :: WhenMatched f x y a -> (a -> WhenMatched f x y b) -> WhenMatched f x y b #

(>>) :: WhenMatched f x y a -> WhenMatched f x y b -> WhenMatched f x y b #

return :: a -> WhenMatched f x y a #

Applicative general combining function

mergeA Source #

Arguments

:: Applicative f 
=> WhenMissing f a c

What to do with keys in m1 but not m2

-> WhenMissing f b c

What to do with keys in m2 but not m1

-> WhenMatched f a b c

What to do with keys in both m1 and m2

-> IntMap a

Map m1

-> IntMap b

Map m2

-> f (IntMap c) 

An applicative version of merge.

mergeA takes two WhenMissing tactics, a WhenMatched tactic and two maps. It uses the tactics to merge the maps. Its behavior is best understood via its fundamental tactics, traverseMaybeMissing and zipWithMaybeAMatched.

Behaves just like merge while allowing Applicative effects. Effects are performed in increasing order of keys.

Consider

mergeA (traverseMaybeMissing g1)
       (traverseMaybeMissing g2)
       (zipWithMaybeAMatched f)
       m1
       m2
g1 k x = let z = if k == 2 then Just ("1" ++ x) else Nothing
         in z <$ putStrLn ("g1 " ++ show (k, x))
g2 k x = let z = if k == 3 then Just ("2" ++ x) else Nothing
         in z <$ putStrLn ("g2 " ++ show (k, x))
f k x y = let z = if k == 6 then Just ("3" ++ x ++ y) else Nothing
          in z <$ putStrLn ("f " ++ show (k, x, y))
m1 = fromList [(2,"a"), (4,"b"), (6,"c"), (8,"d"), (10,"e"), (12,"f")]
m2 = fromList [(3,"g"), (6,"h"), (9,"i"), (12,"j")]

As with merge, the result map is [(2,"1a"), (3,"2g"), (6,"3ch")]. Additionally, g1, g2, and f perform IO effects, printing in increasing order of key.

>>> mergeA (traverseMaybeMissing g1) (traverseMaybeMissing g2) (zipWithMaybeAMatched f) m1 m2
g1 (2,"a")
g2 (3,"g")
g1 (4,"b")
f (6,"c","h")
g1 (8,"d")
g2 (9,"i")
g1 (10,"e")
f (12,"f","j")
fromList [(2,"1a"),(3,"2g"),(6,"3ch")]

The other tactics below are optimizations or simplifications of traverseMaybeMissing for special cases. Most importantly,

When mergeA is given three arguments, it is inlined at the call site. To prevent excessive inlining, you should generally only use mergeA to define custom combining functions.

Examples

Expand
data Pair a = Pair !a !a deriving Functor

instance Applicative Pair where
   pure x = Pair x x
   liftA2 f (Pair x1 y1) (Pair x2 y2) = Pair (f x1 x2) (f y1 y2)

-- | Calculate the left-biased union and intersection of two maps.
unionIntersection :: IntMap a -> IntMap a -> (IntMap a, IntMap a)
unionIntersection m1 m2 =
  case mergeA preserveAndDropMissing preserveAndDropMissing preserveLeftMatched m1 m2 of
    Pair mu mi -> (mu, mi)
  where
    -- use Pair to build the union and intersection together
    preserveAndDropMissing = whenMissing (\_k x -> Pair (Just x) Nothing) (\m -> Pair m empty)
    preserveLeftMatched = zipWithMaybeAMatched (\_k x1 _x2 -> Pair (Just x1) (Just x1))
import Data.Functor.Const (Const(..))
import Data.Monoid (All(..))

-- | Whether the keys of the first map are a subset of the keys of the second map.
keysAreSubsetOf :: IntMap a -> IntMap b -> Bool
keysAreSubsetOf m1 m2 =
  getAll (getConst (mergeA isEmpty dropMissing dropMatched m1 m2))
  where
    isEmpty = whenMissing (\_k _x -> Const (All False)) (\m -> Const (All (null m)))

Since: 0.5.9

WhenMatched tactics

The tactics described for merge work for mergeA as well. Furthermore, the following are available.

zipWithMaybeAMatched :: (Key -> x -> y -> f (Maybe z)) -> WhenMatched f x y z Source #

When a key is found in both maps, apply a function to the key and values, perform the resulting action, and maybe use the result in the merged map.

This is the fundamental WhenMatched tactic.

Since: 0.5.9

zipWithAMatched :: Applicative f => (Key -> x -> y -> f z) -> WhenMatched f x y z Source #

When a key is found in both maps, apply a function to the key and values to produce an action and use its result in the merged map.

Since: 0.5.9

WhenMissing tactics

The tactics described for merge work for mergeA as well. Furthermore, the following are available.

traverseMaybeMissing :: Applicative f => (Key -> x -> f (Maybe y)) -> WhenMissing f x y Source #

Traverse over the entries whose keys are missing from the other map, optionally producing values to put in the result. This is the most powerful WhenMissing tactic, but others are usually more efficient.

Since: 0.5.9

traverseMissing :: Applicative f => (Key -> x -> f y) -> WhenMissing f x y Source #

Traverse over the entries whose keys are missing from the other map.

Since: 0.5.9

filterAMissing :: Applicative f => (Key -> x -> f Bool) -> WhenMissing f x x Source #

Filter the entries whose keys are missing from the other map using some Applicative action.

filterAMissing f = Merge.Lazy.traverseMaybeMissing $
  \k x -> (\b -> guard b *> Just x) <$> f k x

but this should be a little faster.

Since: 0.5.9

whenMissing :: (Key -> x -> f (Maybe y)) -> (IntMap x -> f (IntMap y)) -> WhenMissing f x y Source #

Create a WhenMissing from two functions.

whenMissing must be called with two functions f and g such that g = traverseMaybeWithKey f. g may be a more efficient way of applying f to all key-value pairs in an IntMap.

Warning: It is the caller's responsibility to ensure the above property.

Examples

Expand
preserveMissing :: Applicative f => WhenMissing f x x
preserveMissing = whenMissing f g
  where
    f _k x = pure (Just x)
    g m = pure m
    -- Note that this satisfies g = traverseMaybeWithKey f
import Data.Functor.Const (Const(..))
import Data.Monoid (All(..))

-- For a usage of this, see examples on mergeA
isEmpty :: WhenMissing (Const All) x y
isEmpty = whenMissing f g
  where
    f _k _x = Const (All False)
    g m = Const (All (null m))
    -- Note that this satisfies g = traverseMaybeWithKey f

Since: 0.8.1

Covariant maps for tactics

mapWhenMissing :: forall (f :: Type -> Type) a b x. Monad f => (a -> b) -> WhenMissing f x a -> WhenMissing f x b Source #

Map covariantly over a WhenMissing f x.

Since: 0.5.9

mapWhenMatched :: forall (f :: Type -> Type) a b x y. Functor f => (a -> b) -> WhenMatched f x y a -> WhenMatched f x y b Source #

Map covariantly over a WhenMatched f x y.

Since: 0.5.9

Contravariant maps for tactics

lmapWhenMissing :: forall b a (f :: Type -> Type) x. (b -> a) -> WhenMissing f a x -> WhenMissing f b x Source #

Map contravariantly over a WhenMissing f _ x.

Since: 0.5.9

contramapFirstWhenMatched :: forall b a (f :: Type -> Type) y z. (b -> a) -> WhenMatched f a y z -> WhenMatched f b y z Source #

Map contravariantly over a WhenMatched f _ y z.

Since: 0.5.9

contramapSecondWhenMatched :: forall b a (f :: Type -> Type) x z. (b -> a) -> WhenMatched f x a z -> WhenMatched f x b z Source #

Map contravariantly over a WhenMatched f x _ z.

Since: 0.5.9

Miscellaneous tactic functions

runWhenMatched :: WhenMatched f x y z -> Key -> x -> y -> f (Maybe z) Source #

Along with zipWithMaybeAMatched, witnesses the isomorphism between WhenMatched f x y z and Key -> x -> y -> f (Maybe z).

Since: 0.5.9

runWhenMissing :: WhenMissing f x y -> Key -> x -> f (Maybe y) Source #

Along with traverseMaybeMissing, witnesses the isomorphism between WhenMissing f x y and Key -> x -> f (Maybe y).

Since: 0.5.9