505 lines
13 KiB
Haskell
505 lines
13 KiB
Haskell
{-# LANGUAGE CPP #-}
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
|
{-# LANGUAGE DeriveTraversable #-}
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
-- | `Map` type used to represent records and unions
|
|
|
|
module Dhall.Map
|
|
( -- * Type
|
|
Map
|
|
|
|
-- * Construction
|
|
, singleton
|
|
, fromList
|
|
|
|
-- * Sorting
|
|
, sort
|
|
, isSorted
|
|
|
|
-- * Insertion
|
|
, insert
|
|
, insertWith
|
|
|
|
-- * Deletion/Update
|
|
, delete
|
|
, filter
|
|
, mapMaybe
|
|
|
|
-- * Query
|
|
, lookup
|
|
, member
|
|
, uncons
|
|
|
|
-- * Combine
|
|
, union
|
|
, unionWith
|
|
, intersection
|
|
, intersectionWith
|
|
, difference
|
|
|
|
-- * Traversals
|
|
, mapWithKey
|
|
, traverseWithKey
|
|
, traverseWithKey_
|
|
, unorderedTraverseWithKey_
|
|
, foldMapWithKey
|
|
|
|
-- * Conversions
|
|
, toList
|
|
, toMap
|
|
, keys
|
|
) where
|
|
|
|
import Control.Applicative ((<|>))
|
|
import Data.Data (Data)
|
|
import Data.Foldable (traverse_)
|
|
import Data.Semigroup
|
|
import Prelude hiding (filter, lookup)
|
|
|
|
import qualified Data.Functor
|
|
import qualified Data.Map
|
|
import qualified Data.Set
|
|
import qualified GHC.Exts
|
|
import qualified Prelude
|
|
|
|
{-| A `Map` that remembers the original ordering of keys
|
|
|
|
This is primarily used so that formatting preserves field order
|
|
|
|
This is done primarily to avoid a dependency on @insert-ordered-containers@
|
|
and also to improve performance
|
|
-}
|
|
data Map k v = Map (Data.Map.Map k v) [k]
|
|
deriving (Data)
|
|
|
|
instance (Eq k, Eq v) => Eq (Map k v) where
|
|
(Map m1 ks) == (Map m2 ks') = m1 == m2 && ks == ks'
|
|
{-# INLINABLE (==) #-}
|
|
|
|
instance Functor (Map k) where
|
|
fmap f (Map m ks) = Map (fmap f m) ks
|
|
{-# INLINABLE fmap #-}
|
|
|
|
instance Foldable (Map k) where
|
|
foldr f z (Map m _) = foldr f z m
|
|
{-# INLINABLE foldr #-}
|
|
|
|
foldMap f (Map m _) = foldMap f m
|
|
{-# INLINABLE foldMap #-}
|
|
|
|
instance Traversable (Map k) where
|
|
traverse f (Map m ks) = (\m' -> Map m' ks) <$> traverse f m
|
|
{-# INLINABLE traverse #-}
|
|
|
|
instance Ord k => Data.Semigroup.Semigroup (Map k v) where
|
|
(<>) = union
|
|
{-# INLINABLE (<>) #-}
|
|
|
|
instance Ord k => Monoid (Map k v) where
|
|
mempty = Map Data.Map.empty []
|
|
{-# INLINABLE mempty #-}
|
|
|
|
#if !(MIN_VERSION_base(4,11,0))
|
|
mappend = (<>)
|
|
{-# INLINABLE mappend #-}
|
|
#endif
|
|
|
|
instance (Show k, Show v, Ord k) => Show (Map k v) where
|
|
showsPrec d m =
|
|
showParen (d > 10) (showString "fromList " . showsPrec 11 kvs)
|
|
where
|
|
kvs = toList m
|
|
|
|
instance Ord k => GHC.Exts.IsList (Map k v) where
|
|
type Item (Map k v) = (k, v)
|
|
|
|
fromList = Dhall.Map.fromList
|
|
|
|
toList = Dhall.Map.toList
|
|
|
|
{-| Create a `Map` from a single key-value pair
|
|
|
|
>>> singleton "A" 1
|
|
fromList [("A",1)]
|
|
-}
|
|
singleton :: k -> v -> Map k v
|
|
singleton k v = Map m ks
|
|
where
|
|
m = Data.Map.singleton k v
|
|
|
|
ks = pure k
|
|
{-# INLINABLE singleton #-}
|
|
|
|
{-| Create a `Map` from a list of key-value pairs
|
|
|
|
> fromList empty = mempty
|
|
>
|
|
> fromList (x <|> y) = fromList x <> fromList y
|
|
|
|
>>> fromList [("B",1),("A",2)] -- The map preserves order
|
|
fromList [("B",1),("A",2)]
|
|
>>> fromList [("A",1),("A",2)] -- For duplicates, later values take precedence
|
|
fromList [("A",2)]
|
|
-}
|
|
fromList :: Ord k => [(k, v)] -> Map k v
|
|
fromList kvs = Map m ks
|
|
where
|
|
m = Data.Map.fromList kvs
|
|
|
|
ks = nubOrd (map fst kvs)
|
|
{-# INLINABLE fromList #-}
|
|
|
|
{-| Remove duplicates from a list
|
|
|
|
>>> nubOrd [1,2,3]
|
|
[1,2,3]
|
|
>>> nubOrd [1,1,3]
|
|
[1,3]
|
|
-}
|
|
nubOrd :: Ord k => [k] -> [k]
|
|
nubOrd = go Data.Set.empty
|
|
where
|
|
go _ [] = []
|
|
go set (k:ks)
|
|
| Data.Set.member k set = go set ks
|
|
| otherwise = k : go (Data.Set.insert k set) ks
|
|
{-# INLINABLE nubOrd #-}
|
|
|
|
{-| Sort the keys of a `Map`, forgetting the original ordering
|
|
|
|
> sort (sort x) = sort x
|
|
|
|
>>> sort (fromList [("B",1),("A",2)])
|
|
fromList [("A",2),("B",1)]
|
|
-}
|
|
sort :: Ord k => Map k v -> Map k v
|
|
sort (Map m _) = Map m ks
|
|
where
|
|
ks = Data.Map.keys m
|
|
{-# INLINABLE sort #-}
|
|
|
|
{-| Check if the keys of a `Map` are already sorted
|
|
|
|
> isSorted (sort m) = True
|
|
|
|
>>> isSorted (fromList [("B",1),("A",2)]) -- Sortedness is based only on keys
|
|
False
|
|
>>> isSorted (fromList [("A",2),("B",1)])
|
|
True
|
|
-}
|
|
isSorted :: Eq k => Map k v -> Bool
|
|
isSorted (Map m k) = Data.Map.keys m == k
|
|
{-# INLINABLE isSorted #-}
|
|
|
|
{-| Insert a key-value pair into a `Map`, overriding any previous value stored
|
|
underneath the same key, if present
|
|
|
|
> insert = insertWith (\v _ -> v)
|
|
|
|
>>> insert "C" 1 (fromList [("B",2),("A",3)]) -- Values are inserted on left
|
|
fromList [("C",1),("B",2),("A",3)]
|
|
>>> insert "C" 1 (fromList [("C",2),("A",3)]) -- New value takes precedence
|
|
fromList [("C",1),("A",3)]
|
|
-}
|
|
insert :: Ord k => k -> v -> Map k v -> Map k v
|
|
insert k v (Map m ks) = Map m' ks'
|
|
where
|
|
m' = Data.Map.insert k v m
|
|
|
|
ks' | elem k ks = ks
|
|
| otherwise = k : ks
|
|
{-# INLINABLE insert #-}
|
|
|
|
{-| Insert a key-value pair into a `Map`, using the supplied function to combine
|
|
the new value with any old value underneath the same key, if present
|
|
|
|
>>> insertWith (+) "C" 1 (fromList [("B",2),("A",3)]) -- No collision
|
|
fromList [("C",1),("B",2),("A",3)]
|
|
>>> insertWith (+) "C" 1 (fromList [("C",2),("A",3)]) -- Collision
|
|
fromList [("C",3),("A",3)]
|
|
-}
|
|
insertWith :: Ord k => (v -> v -> v) -> k -> v -> Map k v -> Map k v
|
|
insertWith f k v (Map m ks) = Map m' ks'
|
|
where
|
|
m' = Data.Map.insertWith f k v m
|
|
|
|
ks' | elem k ks = ks
|
|
| otherwise = k : ks
|
|
{-# INLINABLE insertWith #-}
|
|
|
|
{-| Delete a key from a `Map` if present, otherwise return the original `Map`
|
|
|
|
>>> delete "B" (fromList [("C",1),("B",2),("A",3)])
|
|
fromList [("C",1),("A",3)]
|
|
>>> delete "D" (fromList [("C",1),("B",2),("A",3)])
|
|
fromList [("C",1),("B",2),("A",3)]
|
|
-}
|
|
delete :: Ord k => k -> Map k v -> Map k v
|
|
delete k (Map m ks) = Map m' ks'
|
|
where
|
|
m' = Data.Map.delete k m
|
|
|
|
ks' = Prelude.filter (k /=) ks
|
|
{-# INLINABLE delete #-}
|
|
|
|
{-| Keep all values that satisfy the given predicate
|
|
|
|
>>> filter even (fromList [("C",3),("B",2),("A",1)])
|
|
fromList [("B",2)]
|
|
>>> filter odd (fromList [("C",3),("B",2),("A",1)])
|
|
fromList [("C",3),("A",1)]
|
|
-}
|
|
filter :: Ord k => (a -> Bool) -> Map k a -> Map k a
|
|
filter predicate (Map m ks) = Map m' ks'
|
|
where
|
|
m' = Data.Map.filter predicate m
|
|
|
|
set = Data.Map.keysSet m'
|
|
|
|
ks' = Prelude.filter (\k -> Data.Set.member k set) ks
|
|
{-# INLINABLE filter #-}
|
|
|
|
{-| Transform all values in a `Map` using the supplied function, deleting the
|
|
key if the function returns `Nothing`
|
|
|
|
>>> mapMaybe Data.Maybe.listToMaybe (fromList [("C",[1]),("B",[]),("A",[3])])
|
|
fromList [("C",1),("A",3)]
|
|
-}
|
|
mapMaybe :: Ord k => (a -> Maybe b) -> Map k a -> Map k b
|
|
mapMaybe f (Map m ks) = Map m' ks'
|
|
where
|
|
m' = Data.Map.mapMaybe f m
|
|
|
|
set = Data.Map.keysSet m'
|
|
|
|
ks' = Prelude.filter (\k -> Data.Set.member k set) ks
|
|
{-# INLINABLE mapMaybe #-}
|
|
|
|
{-| Retrieve a key from a `Map`
|
|
|
|
> lookup k mempty = empty
|
|
>
|
|
> lookup k (x <> y) = lookup k y <|> lookup k x
|
|
|
|
>>> lookup "A" (fromList [("B",1),("A",2)])
|
|
Just 2
|
|
>>> lookup "C" (fromList [("B",1),("A",2)])
|
|
Nothing
|
|
-}
|
|
lookup :: Ord k => k -> Map k v -> Maybe v
|
|
lookup k (Map m _) = Data.Map.lookup k m
|
|
{-# INLINABLE lookup #-}
|
|
|
|
{-| Retrieve the first key, value of the 'Map', if present,
|
|
and also returning the rest of the 'Map'.
|
|
|
|
> uncons mempty = empty
|
|
>
|
|
> uncons (singleton k v) = (k, v, mempty)
|
|
|
|
>>> uncons (fromList [("C",1),("B",2),("A",3)])
|
|
Just ("C",1,fromList [("B",2),("A",3)])
|
|
>>> uncons (fromList [])
|
|
Nothing
|
|
-}
|
|
uncons :: Ord k => Map k v -> Maybe (k, v, Map k v)
|
|
uncons (Map _ []) = Nothing
|
|
uncons (Map m (k:ks)) = Just (k, m Data.Map.! k, Map (Data.Map.delete k m) ks)
|
|
{-# INLINABLE uncons #-}
|
|
|
|
{-| Check if a key belongs to a `Map`
|
|
|
|
> member k mempty = False
|
|
>
|
|
> member k (x <> y) = member k x || member k y
|
|
|
|
>>> member "A" (fromList [("B",1),("A",2)])
|
|
True
|
|
>>> member "C" (fromList [("B",1),("A",2)])
|
|
False
|
|
-}
|
|
member :: Ord k => k -> Map k v -> Bool
|
|
member k (Map m _) = Data.Map.member k m
|
|
{-# INLINABLE member #-}
|
|
|
|
{-| Combine two `Map`s, preferring keys from the first `Map`
|
|
|
|
> union = unionWith (\v _ -> v)
|
|
|
|
>>> union (fromList [("D",1),("C",2)]) (fromList [("B",3),("A",4)])
|
|
fromList [("D",1),("C",2),("B",3),("A",4)]
|
|
>>> union (fromList [("D",1),("C",2)]) (fromList [("C",3),("A",4)])
|
|
fromList [("D",1),("C",2),("A",4)]
|
|
-}
|
|
union :: Ord k => Map k v -> Map k v -> Map k v
|
|
union (Map mL ksL) (Map mR ksR) = Map m ks
|
|
where
|
|
m = Data.Map.union mL mR
|
|
|
|
setL = Data.Map.keysSet mL
|
|
|
|
ks = ksL <|> Prelude.filter (\k -> Data.Set.notMember k setL) ksR
|
|
{-# INLINABLE union #-}
|
|
|
|
{-| Combine two `Map`s using a combining function for colliding keys
|
|
|
|
>>> unionWith (+) (fromList [("D",1),("C",2)]) (fromList [("B",3),("A",4)])
|
|
fromList [("D",1),("C",2),("B",3),("A",4)]
|
|
>>> unionWith (+) (fromList [("D",1),("C",2)]) (fromList [("C",3),("A",4)])
|
|
fromList [("D",1),("C",5),("A",4)]
|
|
-}
|
|
unionWith :: Ord k => (v -> v -> v) -> Map k v -> Map k v -> Map k v
|
|
unionWith combine (Map mL ksL) (Map mR ksR) = Map m ks
|
|
where
|
|
m = Data.Map.unionWith combine mL mR
|
|
|
|
setL = Data.Map.keysSet mL
|
|
|
|
ks = ksL <|> Prelude.filter (\k -> Data.Set.notMember k setL) ksR
|
|
{-# INLINABLE unionWith #-}
|
|
|
|
{-| Combine two `Map` on their shared keys, keeping the value from the first
|
|
`Map`
|
|
|
|
> intersection = intersectionWith (\v _ -> v)
|
|
|
|
>>> intersection (fromList [("C",1),("B",2)]) (fromList [("B",3),("A",4)])
|
|
fromList [("B",2)]
|
|
-}
|
|
intersection :: Ord k => Map k a -> Map k b -> Map k a
|
|
intersection (Map mL ksL) (Map mR _) = Map m ks
|
|
where
|
|
m = Data.Map.intersection mL mR
|
|
|
|
setL = Data.Map.keysSet mL
|
|
setR = Data.Map.keysSet mR
|
|
set = Data.Set.intersection setL setR
|
|
ks = Prelude.filter (\k -> Data.Set.member k set) ksL
|
|
{-# INLINABLE intersection #-}
|
|
|
|
{-| Combine two `Map`s on their shared keys, using the supplied function to
|
|
combine values from the first and second `Map`
|
|
|
|
>>> intersectionWith (+) (fromList [("C",1),("B",2)]) (fromList [("B",3),("A",4)])
|
|
fromList [("B",5)]
|
|
-}
|
|
intersectionWith :: Ord k => (a -> b -> c) -> Map k a -> Map k b -> Map k c
|
|
intersectionWith combine (Map mL ksL) (Map mR _) = Map m ks
|
|
where
|
|
m = Data.Map.intersectionWith combine mL mR
|
|
|
|
setL = Data.Map.keysSet mL
|
|
setR = Data.Map.keysSet mR
|
|
set = Data.Set.intersection setL setR
|
|
ks = Prelude.filter (\k -> Data.Set.member k set) ksL
|
|
{-# INLINABLE intersectionWith #-}
|
|
|
|
{-| Compute the difference of two `Map`s by subtracting all keys from the
|
|
second `Map` from the first `Map`
|
|
|
|
>>> difference (fromList [("C",1),("B",2)]) (fromList [("B",3),("A",4)])
|
|
fromList [("C",1)]
|
|
-}
|
|
difference :: Ord k => Map k a -> Map k b -> Map k a
|
|
difference (Map mL ksL) (Map mR _) = Map m ks
|
|
where
|
|
m = Data.Map.difference mL mR
|
|
|
|
setR = Data.Map.keysSet mR
|
|
|
|
ks = Prelude.filter (\k -> Data.Set.notMember k setR) ksL
|
|
{-# INLINABLE difference #-}
|
|
|
|
{-| Fold all of the key-value pairs in a `Map`, in their original order
|
|
|
|
>>> foldMapWithKey (,) (fromList [("B",[1]),("A",[2])])
|
|
("BA",[1,2])
|
|
-}
|
|
foldMapWithKey :: (Monoid m, Ord k) => (k -> a -> m) -> Map k a -> m
|
|
foldMapWithKey f m = foldMap (uncurry f) (toList m)
|
|
{-# INLINABLE foldMapWithKey #-}
|
|
|
|
{-| Transform the values of a `Map` using their corresponding key
|
|
|
|
> mapWithKey (pure id) = id
|
|
>
|
|
> mapWithKey (liftA2 (.) f g) = mapWithKey f . mapWithKey g
|
|
|
|
> mapWithKey f mempty = mempty
|
|
>
|
|
> mapWithKey f (x <> y) = mapWithKey f x <> mapWithKey f y
|
|
|
|
>>> mapWithKey (,) (fromList [("B",1),("A",2)])
|
|
fromList [("B",("B",1)),("A",("A",2))]
|
|
-}
|
|
mapWithKey :: (k -> a -> b) -> Map k a -> Map k b
|
|
mapWithKey f (Map m ks) = Map m' ks
|
|
where
|
|
m' = Data.Map.mapWithKey f m
|
|
{-# INLINABLE mapWithKey #-}
|
|
|
|
{-| Traverse all of the key-value pairs in a `Map`, in their original order
|
|
|
|
>>> traverseWithKey (,) (fromList [("B",1),("A",2)])
|
|
("BA",fromList [("B",1),("A",2)])
|
|
-}
|
|
traverseWithKey
|
|
:: Ord k => Applicative f => (k -> a -> f b) -> Map k a -> f (Map k b)
|
|
traverseWithKey f m =
|
|
fmap fromList (traverse f' (toList m))
|
|
where
|
|
f' (k, a) = fmap ((,) k) (f k a)
|
|
{-# INLINABLE traverseWithKey #-}
|
|
|
|
{-| Traverse all of the key-value pairs in a `Map`, in their original order
|
|
where the result of the computation can be forgotten.
|
|
|
|
>>> traverseWithKey_ (\k v -> print (k, v)) (fromList [("B",1),("A",2)])
|
|
("B",1)
|
|
("A",2)
|
|
-}
|
|
traverseWithKey_
|
|
:: Ord k => Applicative f => (k -> a -> f ()) -> Map k a -> f ()
|
|
traverseWithKey_ f m = Data.Functor.void (traverseWithKey f m)
|
|
{-# INLINABLE traverseWithKey_ #-}
|
|
|
|
{-| Travese all of the key-value pairs in a 'Map', not preserving their
|
|
original order, where the result of the computation can be forgotten.
|
|
|
|
Note that this is an optimisation over 'traverseWithKey_' since we do
|
|
not care in what order we traverse the pairs.
|
|
-}
|
|
unorderedTraverseWithKey_
|
|
:: Ord k => Applicative f => (k -> a -> f ()) -> Map k a -> f ()
|
|
unorderedTraverseWithKey_ f = Data.Functor.void . traverse_ (uncurry f) . toList
|
|
{-# INLINABLE unorderedTraverseWithKey_ #-}
|
|
|
|
{-| Convert a `Map` to a list of key-value pairs in the original order of keys
|
|
|
|
>>> toList (fromList [("B",1),("A",2)])
|
|
[("B",1),("A",2)]
|
|
-}
|
|
toList :: Ord k => Map k v -> [(k, v)]
|
|
toList (Map m ks) = fmap (\k -> (k, m Data.Map.! k)) ks
|
|
{-# INLINABLE toList #-}
|
|
|
|
{-| Convert a @"Dhall.Map".`Map`@ to a @"Data.Map".`Data.Map.Map`@
|
|
|
|
>>> toMap (fromList [("B",1),("A",2)]) -- Order is lost upon conversion
|
|
fromList [("A",2),("B",1)]
|
|
-}
|
|
toMap :: Map k v -> Data.Map.Map k v
|
|
toMap (Map m _) = m
|
|
{-# INLINABLE toMap #-}
|
|
|
|
{-| Return the keys from a `Map` in their original order
|
|
|
|
>>> keys (fromList [("B",1),("A",2)])
|
|
["B","A"]
|
|
-}
|
|
keys :: Map k v -> [k]
|
|
keys (Map _ ks) = ks
|
|
{-# INLINABLE keys #-}
|