-
Notifications
You must be signed in to change notification settings - Fork 2.8k
server: validation code simplifications #4585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,10 +71,10 @@ module Hasura.GraphQL.Validate.Types | |
| , ReusableVariableValues | ||
|
|
||
| , QueryReusability(..) | ||
| , _Reusable | ||
| , _NotReusable | ||
| , MonadReusability(..) | ||
| , MonadReusability | ||
| , ReusabilityT | ||
| , markNotReusable | ||
| , recordVariableUse | ||
| , runReusabilityT | ||
| , runReusabilityTWith | ||
| , evalReusabilityT | ||
|
|
@@ -95,8 +95,6 @@ import qualified Language.GraphQL.Draft.Syntax as G | |
| import qualified Language.GraphQL.Draft.TH as G | ||
| import qualified Language.Haskell.TH.Syntax as TH | ||
|
|
||
| import Control.Lens (makePrisms) | ||
|
|
||
| import qualified Hasura.RQL.Types.Column as RQL | ||
|
|
||
| import Hasura.GraphQL.Utils | ||
|
|
@@ -247,9 +245,6 @@ instance EquatableGType ObjTyInfo where | |
| (G.NamedType, Set.HashSet G.NamedType, Map.HashMap G.Name (G.Name, G.GType, ParamMap)) | ||
| getEqProps a = (,,) (_otiName a) (_otiImplIFaces a) (Map.map getEqProps (_otiFields a)) | ||
|
|
||
| instance Monoid ObjTyInfo where | ||
| mempty = ObjTyInfo Nothing (G.NamedType "") Set.empty Map.empty | ||
|
|
||
| instance Semigroup ObjTyInfo where | ||
| objA <> objB = | ||
| objA { _otiFields = Map.union (_otiFields objA) (_otiFields objB) | ||
|
|
@@ -303,14 +298,6 @@ instance EquatableGType IFaceTyInfo where | |
| (G.NamedType, Map.HashMap G.Name (G.Name, G.GType, ParamMap)) | ||
| getEqProps a = (,) (_ifName a) (Map.map getEqProps (_ifFields a)) | ||
|
|
||
| instance Monoid IFaceTyInfo where | ||
| mempty = IFaceTyInfo Nothing (G.NamedType "") Map.empty | ||
|
|
||
| instance Semigroup IFaceTyInfo where | ||
| objA <> objB = | ||
| objA { _ifFields = Map.union (_ifFields objA) (_ifFields objB) | ||
| } | ||
|
|
||
| fromIFaceDef :: G.InterfaceTypeDefinition -> TypeLoc -> IFaceTyInfo | ||
| fromIFaceDef (G.InterfaceTypeDefinition descM n _ flds) loc = | ||
| mkIFaceTyInfo descM (G.NamedType n) fldMap loc | ||
|
|
@@ -331,14 +318,6 @@ instance EquatableGType UnionTyInfo where | |
| (G.NamedType, Set.HashSet G.NamedType) | ||
| getEqProps a = (,) (_utiName a) (_utiMemberTypes a) | ||
|
|
||
| instance Monoid UnionTyInfo where | ||
| mempty = UnionTyInfo Nothing (G.NamedType "") Set.empty | ||
|
|
||
| instance Semigroup UnionTyInfo where | ||
| objA <> objB = | ||
| objA { _utiMemberTypes = Set.union (_utiMemberTypes objA) (_utiMemberTypes objB) | ||
| } | ||
|
|
||
| fromUnionTyDef :: G.UnionTypeDefinition -> UnionTyInfo | ||
| fromUnionTyDef (G.UnionTypeDefinition descM n _ mt) = UnionTyInfo descM (G.NamedType n) $ Set.fromList mt | ||
|
|
||
|
|
@@ -810,36 +789,28 @@ data QueryReusability | |
| = Reusable !ReusableVariableTypes | ||
| | NotReusable | ||
| deriving (Show, Eq) | ||
| $(makePrisms ''QueryReusability) | ||
|
|
||
| instance Semigroup QueryReusability where | ||
| Reusable a <> Reusable b = Reusable (a <> b) | ||
| _ <> _ = NotReusable | ||
| instance Monoid QueryReusability where | ||
| mempty = Reusable mempty | ||
|
|
||
| class (Monad m) => MonadReusability m where | ||
| recordVariableUse :: G.Variable -> RQL.PGColumnType -> m () | ||
| markNotReusable :: m () | ||
| type MonadReusability m = MonadState QueryReusability m | ||
|
|
||
| instance (MonadReusability m) => MonadReusability (ReaderT r m) where | ||
| recordVariableUse a b = lift $ recordVariableUse a b | ||
| markNotReusable = lift markNotReusable | ||
| type ReusabilityT m a = StateT QueryReusability m a | ||
|
Comment on lines
-822
to
+795
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I normally recommend against doing this. Why? Two reasons:
Perhaps the above two points seem like small potatoes, and maybe they are. But I don’t think they’re inconsequential enough to justify switching away from the abstraction just to be a little bit simpler! One improvement that would be nice would be to switch from Fortunately, recent versions of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for this background, Alexis. I completely agree with the points you raise. I am not as well-versed in the space leakage of the various monad transformers, so what you wrote is very informative. The reason I opened this PR nonetheless is that I think in this case the abstraction of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW I agree with you 100% that the “mtl-style” approach to effect composition is way, way too complicated, and we want something better. That is in fact precisely why I have written eff: I think monad transformers are the single largest obstacle to new Haskell programmers who want to get productive with the language, and I think algebraic effects are a better design. Unfortunately, existing approaches to algebraic effects in Haskell aren’t very efficient, so eff is also a project in making them fast. And eff is fast… but it is fast because it depends on some modifications to GHC. I have an open GHC proposal to get those modifications merged, but until then we’re stuck with what we have. So yes, less tangentially, I’m happy to include some more documentation wherever it would help. Please feel free to add some extra documentation wherever it would be helpful! (Or, for that matter, request for someone else to add some documentation if you don’t feel like you understand it enough to do it yourself.) |
||
|
|
||
| newtype ReusabilityT m a = ReusabilityT { unReusabilityT :: StateT QueryReusability m a } | ||
| deriving (Functor, Applicative, Monad, MonadError e, MonadReader r, MonadIO) | ||
| markNotReusable :: MonadReusability m => m () | ||
| markNotReusable = put NotReusable | ||
|
|
||
| instance (Monad m) => MonadReusability (ReusabilityT m) where | ||
| recordVariableUse varName varType = ReusabilityT $ | ||
| modify' (<> Reusable (ReusableVariableTypes $ Map.singleton varName varType)) | ||
| markNotReusable = ReusabilityT $ put NotReusable | ||
| recordVariableUse :: MonadReusability m => G.Variable -> RQL.PGColumnType -> m () | ||
| recordVariableUse varName varType = modify' combine | ||
| where | ||
| combine :: QueryReusability -> QueryReusability | ||
| combine (Reusable (ReusableVariableTypes ts)) = | ||
| Reusable (ReusableVariableTypes (Map.insert varName varType ts)) | ||
| combine NotReusable = NotReusable | ||
|
|
||
| runReusabilityT :: ReusabilityT m a -> m (a, QueryReusability) | ||
| runReusabilityT = runReusabilityTWith mempty | ||
| runReusabilityT = runReusabilityTWith (Reusable mempty) | ||
|
|
||
| -- | Like 'runReusabilityT', but starting from an existing 'QueryReusability' state. | ||
| runReusabilityTWith :: QueryReusability -> ReusabilityT m a -> m (a, QueryReusability) | ||
| runReusabilityTWith initialReusability = flip runStateT initialReusability . unReusabilityT | ||
| runReusabilityTWith initialReusability = flip runStateT initialReusability | ||
|
|
||
| evalReusabilityT :: (Monad m) => ReusabilityT m a -> m a | ||
| evalReusabilityT = flip evalStateT mempty . unReusabilityT | ||
| evalReusabilityT = flip evalStateT (Reusable mempty) | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You say this “isn’t actually a monoid,” but that’s just not true; this is a monoid. It satisfies all the monoid laws. It wouldn’t be a monoid if we defined
mempty = NotReusable, but we don’t. As-written, it’s a perfectly cromulent monoid.(You’re right that the other instances aren’t monoids, and we should get rid of those. But this one seems fine to me.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, having taken a second look, you are right. So it makes sense to treat
QueryReusabilityas a monoid.