149 lines
5.1 KiB
Haskell
149 lines
5.1 KiB
Haskell
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
|
|
|
{-# LANGUAGE ExplicitForAll #-}
|
|
|
|
-- | This module contains a map data structure, that preserves insertion order.
|
|
-- Some definitions conflict with functions from prelude, so this module should
|
|
-- probably be imported qualified.
|
|
module Language.GraphQL.Execute.OrderedMap
|
|
( OrderedMap
|
|
, elems
|
|
, empty
|
|
, insert
|
|
, foldlWithKey'
|
|
, keys
|
|
, lookup
|
|
, replace
|
|
, singleton
|
|
, size
|
|
, toList
|
|
, traverseMaybe
|
|
) where
|
|
|
|
import qualified Data.Foldable as Foldable
|
|
import Data.HashMap.Strict (HashMap, (!))
|
|
import qualified Data.HashMap.Strict as HashMap
|
|
import Data.Text (Text)
|
|
import Data.Vector (Vector)
|
|
import qualified Data.Vector as Vector
|
|
import Prelude hiding (filter, lookup)
|
|
|
|
-- | This map associates values with the given text keys. Insertion order is
|
|
-- preserved. When inserting a value with a key, that is already available in
|
|
-- the map, the existing value isn't overridden, but combined with the new value
|
|
-- using its 'Semigroup' instance.
|
|
--
|
|
-- Internally this map uses an array with keys to preserve the order and an
|
|
-- unorded map with key-value pairs.
|
|
data OrderedMap v = OrderedMap (Vector Text) (HashMap Text v)
|
|
deriving (Eq)
|
|
|
|
instance Functor OrderedMap where
|
|
fmap f (OrderedMap vector hashMap) = OrderedMap vector $ fmap f hashMap
|
|
|
|
instance Foldable OrderedMap where
|
|
foldr f = foldrWithKey $ const f
|
|
null (OrderedMap vector _) = Vector.null vector
|
|
|
|
instance Semigroup v => Semigroup (OrderedMap v) where
|
|
(<>) = foldlWithKey'
|
|
$ \accumulator key value -> insert key value accumulator
|
|
|
|
instance Semigroup v => Monoid (OrderedMap v) where
|
|
mempty = empty
|
|
|
|
instance Traversable OrderedMap where
|
|
traverse f (OrderedMap vector hashMap) = OrderedMap vector
|
|
<$> traverse f hashMap
|
|
|
|
instance Show v => Show (OrderedMap v) where
|
|
showsPrec precedence map' = showParen (precedence > 10)
|
|
$ showString "fromList " . shows (toList map')
|
|
|
|
-- * Construction
|
|
|
|
-- | Constructs a map with a single element.
|
|
singleton :: forall v. Text -> v -> OrderedMap v
|
|
singleton key value = OrderedMap (Vector.singleton key)
|
|
$ HashMap.singleton key value
|
|
|
|
-- | Constructs an empty map.
|
|
empty :: forall v. OrderedMap v
|
|
empty = OrderedMap mempty mempty
|
|
|
|
-- * Traversal
|
|
|
|
-- | Reduces this map by applying a binary operator from right to left to all
|
|
-- elements, using the given starting value.
|
|
foldrWithKey :: forall v a. (Text -> v -> a -> a) -> a -> OrderedMap v -> a
|
|
foldrWithKey f initial (OrderedMap vector hashMap) = foldr go initial vector
|
|
where
|
|
go key = f key (hashMap ! key)
|
|
|
|
-- | Reduces this map by applying a binary operator from left to right to all
|
|
-- elements, using the given starting value.
|
|
foldlWithKey' :: forall v a. (a -> Text -> v -> a) -> a -> OrderedMap v -> a
|
|
foldlWithKey' f initial (OrderedMap vector hashMap) =
|
|
Vector.foldl' go initial vector
|
|
where
|
|
go accumulator key = f accumulator key (hashMap ! key)
|
|
|
|
-- | Traverse over the elements and collect the 'Just' results.
|
|
traverseMaybe
|
|
:: Applicative f
|
|
=> forall a
|
|
. (a -> f (Maybe b))
|
|
-> OrderedMap a
|
|
-> f (OrderedMap b)
|
|
traverseMaybe f orderedMap = foldlWithKey' filter empty
|
|
<$> traverse f orderedMap
|
|
where
|
|
filter accumulator key (Just value) = replace key value accumulator
|
|
filter accumulator _ Nothing = accumulator
|
|
|
|
-- * Lists
|
|
|
|
-- | Converts this map to the list of key-value pairs.
|
|
toList :: forall v. OrderedMap v -> [(Text, v)]
|
|
toList = foldrWithKey ((.) (:) . (,)) []
|
|
|
|
-- | Returns a list with all keys in this map.
|
|
keys :: forall v. OrderedMap v -> [Text]
|
|
keys (OrderedMap vector _) = Foldable.toList vector
|
|
|
|
-- | Returns a list with all elements in this map.
|
|
elems :: forall v. OrderedMap v -> [v]
|
|
elems = fmap snd . toList
|
|
|
|
-- * Basic interface
|
|
|
|
-- | Associates the specified value with the specified key in this map. If this
|
|
-- map previously contained a mapping for the key, the existing and new values
|
|
-- are combined.
|
|
insert :: Semigroup v => Text -> v -> OrderedMap v -> OrderedMap v
|
|
insert key value (OrderedMap vector hashMap)
|
|
| Just available <- HashMap.lookup key hashMap = OrderedMap vector
|
|
$ HashMap.insert key (available <> value) hashMap
|
|
| otherwise = OrderedMap (Vector.snoc vector key)
|
|
$ HashMap.insert key value hashMap
|
|
|
|
-- | Associates the specified value with the specified key in this map. If this
|
|
-- map previously contained a mapping for the key, the existing value is
|
|
-- replaced by the new one.
|
|
replace :: Text -> v -> OrderedMap v -> OrderedMap v
|
|
replace key value (OrderedMap vector hashMap)
|
|
| HashMap.member key hashMap = OrderedMap vector
|
|
$ HashMap.insert key value hashMap
|
|
| otherwise = OrderedMap (Vector.snoc vector key)
|
|
$ HashMap.insert key value hashMap
|
|
|
|
-- | Gives the size of this map, i.e. number of elements in it.
|
|
size :: forall v. OrderedMap v -> Int
|
|
size (OrderedMap vector _) = Vector.length vector
|
|
|
|
-- | Looks up a value in this map by key.
|
|
lookup :: forall v. Text -> OrderedMap v -> Maybe v
|
|
lookup key (OrderedMap _ hashMap) = HashMap.lookup key hashMap
|