Replace Map with OrderedMap

This commit is contained in:
Eugen Wissner 2021-02-19 08:09:04 +01:00
parent d74e27e903
commit 10e4d64052
6 changed files with 44 additions and 19 deletions

View File

@ -7,6 +7,9 @@ and this project adheres to
[Haskell Package Versioning Policy](https://pvp.haskell.org/). [Haskell Package Versioning Policy](https://pvp.haskell.org/).
## [Unreleased] ## [Unreleased]
### Added
- `Language.GraphQL.Execute.OrderedMap` is a map data structure, that preserves
insertion order.
## [0.11.1.0] - 2021-02-07 ## [0.11.1.0] - 2021-02-07
### Added ### Added

View File

@ -19,7 +19,6 @@ import qualified Data.Aeson as Aeson
import Data.Int (Int32) import Data.Int (Int32)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap import qualified Data.HashMap.Strict as HashMap
import Data.Map.Strict (Map)
import Data.String (IsString(..)) import Data.String (IsString(..))
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text.Lazy as Text.Lazy import qualified Data.Text.Lazy as Text.Lazy
@ -27,6 +26,8 @@ import qualified Data.Text.Lazy.Builder as Text.Builder
import qualified Data.Text.Lazy.Builder.Int as Text.Builder import qualified Data.Text.Lazy.Builder.Int as Text.Builder
import Data.Scientific (toBoundedInteger, toRealFloat) import Data.Scientific (toBoundedInteger, toRealFloat)
import Language.GraphQL.AST (Name) import Language.GraphQL.AST (Name)
import Language.GraphQL.Execute.OrderedMap (OrderedMap)
import qualified Language.GraphQL.Execute.OrderedMap as OrderedMap
import qualified Language.GraphQL.Type as Type import qualified Language.GraphQL.Type as Type
import qualified Language.GraphQL.Type.In as In import qualified Language.GraphQL.Type.In as In
import qualified Language.GraphQL.Type.Out as Out import qualified Language.GraphQL.Type.Out as Out
@ -209,7 +210,7 @@ data Output a
| Boolean Bool | Boolean Bool
| Enum Name | Enum Name
| List [a] | List [a]
| Object (Map Name a) | Object (OrderedMap a)
deriving (Eq, Show) deriving (Eq, Show)
instance forall a. IsString (Output a) where instance forall a. IsString (Output a) where
@ -229,6 +230,9 @@ instance Serialize Aeson.Value where
, Boolean boolean <- value = Just $ Aeson.Bool boolean , Boolean boolean <- value = Just $ Aeson.Bool boolean
serialize _ (Enum enum) = Just $ Aeson.String enum serialize _ (Enum enum) = Just $ Aeson.String enum
serialize _ (List list) = Just $ Aeson.toJSON list serialize _ (List list) = Just $ Aeson.toJSON list
serialize _ (Object object) = Just $ Aeson.toJSON object serialize _ (Object object) = Just
$ Aeson.object
$ OrderedMap.toList
$ Aeson.toJSON <$> object
serialize _ _ = Nothing serialize _ _ = Nothing
null = Aeson.Null null = Aeson.Null

View File

@ -13,16 +13,16 @@ import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (runReaderT) import Control.Monad.Trans.Reader (runReaderT)
import Control.Monad.Trans.State (gets) import Control.Monad.Trans.State (gets)
import Data.List.NonEmpty (NonEmpty(..)) import Data.List.NonEmpty (NonEmpty(..))
import Data.Map.Strict (Map)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap import qualified Data.HashMap.Strict as HashMap
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Sequence (Seq(..)) import Data.Sequence (Seq(..))
import qualified Data.Text as Text import qualified Data.Text as Text
import Language.GraphQL.AST (Name) import Language.GraphQL.AST (Name)
import Language.GraphQL.Error import Language.GraphQL.Error
import Language.GraphQL.Execute.Coerce import Language.GraphQL.Execute.Coerce
import Language.GraphQL.Execute.OrderedMap (OrderedMap)
import qualified Language.GraphQL.Execute.OrderedMap as OrderedMap
import qualified Language.GraphQL.Execute.Transform as Transform import qualified Language.GraphQL.Execute.Transform as Transform
import qualified Language.GraphQL.Type as Type import qualified Language.GraphQL.Type as Type
import qualified Language.GraphQL.Type.In as In import qualified Language.GraphQL.Type.In as In
@ -51,17 +51,17 @@ resolveFieldValue result args resolver =
collectFields :: Monad m collectFields :: Monad m
=> Out.ObjectType m => Out.ObjectType m
-> Seq (Transform.Selection m) -> Seq (Transform.Selection m)
-> Map Name (NonEmpty (Transform.Field m)) -> OrderedMap (NonEmpty (Transform.Field m))
collectFields objectType = foldl forEach Map.empty collectFields objectType = foldl forEach OrderedMap.empty
where where
forEach groupedFields (Transform.SelectionField field) = forEach groupedFields (Transform.SelectionField field) =
let responseKey = aliasOrName field let responseKey = aliasOrName field
in Map.insertWith (<>) responseKey (field :| []) groupedFields in OrderedMap.insert responseKey (field :| []) groupedFields
forEach groupedFields (Transform.SelectionFragment selectionFragment) forEach groupedFields (Transform.SelectionFragment selectionFragment)
| Transform.Fragment fragmentType fragmentSelectionSet <- selectionFragment | Transform.Fragment fragmentType fragmentSelectionSet <- selectionFragment
, Internal.doesFragmentTypeApply fragmentType objectType = , Internal.doesFragmentTypeApply fragmentType objectType =
let fragmentGroupedFieldSet = collectFields objectType fragmentSelectionSet let fragmentGroupedFieldSet = collectFields objectType fragmentSelectionSet
in Map.unionWith (<>) groupedFields fragmentGroupedFieldSet in groupedFields <> fragmentGroupedFieldSet
| otherwise = groupedFields | otherwise = groupedFields
aliasOrName :: forall m. Transform.Field m -> Name aliasOrName :: forall m. Transform.Field m -> Name
@ -170,10 +170,10 @@ executeSelectionSet :: (MonadCatch m, Serialize a)
-> CollectErrsT m a -> CollectErrsT m a
executeSelectionSet result objectType@(Out.ObjectType _ _ _ resolvers) selectionSet = do executeSelectionSet result objectType@(Out.ObjectType _ _ _ resolvers) selectionSet = do
let fields = collectFields objectType selectionSet let fields = collectFields objectType selectionSet
resolvedValues <- Map.traverseMaybeWithKey forEach fields resolvedValues <- OrderedMap.traverseMaybe forEach fields
coerceResult (Out.NonNullObjectType objectType) $ Object resolvedValues coerceResult (Out.NonNullObjectType objectType) $ Object resolvedValues
where where
forEach _ fields@(field :| _) = forEach fields@(field :| _) =
let Transform.Field _ name _ _ = field let Transform.Field _ name _ _ = field
in traverse (tryResolver fields) $ lookupResolver name in traverse (tryResolver fields) $ lookupResolver name
lookupResolver = flip HashMap.lookup resolvers lookupResolver = flip HashMap.lookup resolvers

View File

@ -15,8 +15,10 @@ module Language.GraphQL.Execute.OrderedMap
, foldlWithKey' , foldlWithKey'
, keys , keys
, lookup , lookup
, replace
, singleton , singleton
, size , size
, toList
, traverseMaybe , traverseMaybe
) where ) where
@ -49,8 +51,8 @@ instance Semigroup v => Semigroup (OrderedMap v) where
(<>) = foldlWithKey' (<>) = foldlWithKey'
$ \accumulator key value -> insert key value accumulator $ \accumulator key value -> insert key value accumulator
instance Monoid v => Monoid (OrderedMap v) where instance Semigroup v => Monoid (OrderedMap v) where
mempty = OrderedMap mempty mempty mempty = empty
instance Traversable OrderedMap where instance Traversable OrderedMap where
traverse f (OrderedMap vector hashMap) = OrderedMap vector traverse f (OrderedMap vector hashMap) = OrderedMap vector
@ -68,8 +70,8 @@ singleton key value = OrderedMap (Vector.singleton key)
$ HashMap.singleton key value $ HashMap.singleton key value
-- | Constructs an empty map. -- | Constructs an empty map.
empty :: Monoid v => OrderedMap v empty :: forall v. OrderedMap v
empty = mempty empty = OrderedMap mempty mempty
-- * Traversal -- * Traversal
@ -90,7 +92,7 @@ foldlWithKey' f initial (OrderedMap vector hashMap) =
-- | Traverse over the elements and collect the 'Just' results. -- | Traverse over the elements and collect the 'Just' results.
traverseMaybe traverseMaybe
:: (Applicative f, Monoid b) :: Applicative f
=> forall a => forall a
. (a -> f (Maybe b)) . (a -> f (Maybe b))
-> OrderedMap a -> OrderedMap a
@ -98,7 +100,7 @@ traverseMaybe
traverseMaybe f orderedMap = foldlWithKey' filter empty traverseMaybe f orderedMap = foldlWithKey' filter empty
<$> traverse f orderedMap <$> traverse f orderedMap
where where
filter accumulator key (Just value) = insert key value accumulator filter accumulator key (Just value) = replace key value accumulator
filter accumulator _ Nothing = accumulator filter accumulator _ Nothing = accumulator
-- * Lists -- * Lists
@ -127,6 +129,16 @@ insert key value (OrderedMap vector hashMap)
| otherwise = OrderedMap (Vector.snoc vector key) | otherwise = OrderedMap (Vector.snoc vector key)
$ HashMap.insert key value hashMap $ 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. -- | Gives the size of this map, i.e. number of elements in it.
size :: forall v. OrderedMap v -> Int size :: forall v. OrderedMap v -> Int
size (OrderedMap vector _) = Vector.length vector size (OrderedMap vector _) = Vector.length vector

View File

@ -13,7 +13,6 @@ import Control.Monad.Catch (Exception(..), MonadCatch(..))
import Control.Monad.Trans.Reader (ReaderT(..), runReaderT) import Control.Monad.Trans.Reader (ReaderT(..), runReaderT)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap import qualified Data.HashMap.Strict as HashMap
import qualified Data.Map.Strict as Map
import qualified Data.List.NonEmpty as NonEmpty import qualified Data.List.NonEmpty as NonEmpty
import Data.Sequence (Seq(..)) import Data.Sequence (Seq(..))
import Data.Text (Text) import Data.Text (Text)
@ -21,6 +20,7 @@ import qualified Data.Text as Text
import Language.GraphQL.AST (Name) import Language.GraphQL.AST (Name)
import Language.GraphQL.Execute.Coerce import Language.GraphQL.Execute.Coerce
import Language.GraphQL.Execute.Execution import Language.GraphQL.Execute.Execution
import qualified Language.GraphQL.Execute.OrderedMap as OrderedMap
import qualified Language.GraphQL.Execute.Transform as Transform import qualified Language.GraphQL.Execute.Transform as Transform
import Language.GraphQL.Error import Language.GraphQL.Error
import qualified Language.GraphQL.Type.Definition as Definition import qualified Language.GraphQL.Type.Definition as Definition
@ -55,7 +55,7 @@ createSourceEventStream :: MonadCatch m
-> Seq (Transform.Selection m) -> Seq (Transform.Selection m)
-> m (Either Text (Out.SourceEventStream m)) -> m (Either Text (Out.SourceEventStream m))
createSourceEventStream _types subscriptionType@(Out.ObjectType _ _ _ fieldTypes) fields createSourceEventStream _types subscriptionType@(Out.ObjectType _ _ _ fieldTypes) fields
| [fieldGroup] <- Map.elems groupedFieldSet | [fieldGroup] <- OrderedMap.elems groupedFieldSet
, Transform.Field _ fieldName arguments' _ <- NonEmpty.head fieldGroup , Transform.Field _ fieldName arguments' _ <- NonEmpty.head fieldGroup
, resolverT <- fieldTypes HashMap.! fieldName , resolverT <- fieldTypes HashMap.! fieldName
, Out.EventStreamResolver fieldDefinition _ resolver <- resolverT , Out.EventStreamResolver fieldDefinition _ resolver <- resolverT

View File

@ -64,3 +64,9 @@ spec =
$ OrderedMap.insert "key2" "2" $ OrderedMap.insert "key2" "2"
$ OrderedMap.singleton "key1" "1" $ OrderedMap.singleton "key1" "1"
in (map1 <> map2) `shouldBe` expected in (map1 <> map2) `shouldBe` expected
it "replaces existing values" $
let key = "key"
actual = OrderedMap.replace key ("2" :: String)
$ OrderedMap.singleton key ("1" :: String)
in OrderedMap.lookup key actual `shouldBe` Just "2"