Skip to content
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

[RFC] Type system ordering of: object interfaces, directive arguments, input object fields, enum values #1063

Open
wants to merge 5 commits into
base: maintain-order
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 33 additions & 26 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,17 +302,18 @@ enumerable. GraphQL offers an `Enum` type in those cases, where the type
specifies the space of valid responses.

Scalars and Enums form the leaves in response trees; the intermediate levels are
`Object` types, which define a set of fields, where each field is another type
in the system, allowing the definition of arbitrary type hierarchies.
`Object` types, which define an ordered set of fields, where each field is
Comment on lines 304 to +305

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t objects (and interfaces, and input objects) have an ordered map of fields? The field names are unique, not the field definitions as a whole. foo: Int, foo: String would be a set of two different fields that happen to share a name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I raised in the WG actually; but that change is beyond the scope of this PR.

another type in the system, allowing the definition of arbitrary type
hierarchies.

GraphQL supports two abstract types: interfaces and unions.

An `Interface` defines a list of fields; `Object` types and other Interface
types which implement this Interface are guaranteed to implement those fields.
Whenever a field claims it will return an Interface type, it will return a valid
implementing Object type during execution.
An `Interface` defines an ordered set of fields; `Object` types and other
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO Interface/Object is not really an ordered set? Maybe it is? It feels a little bit annoying for maintainers to feel like they must maintain order (does the order of fields in the object need to match the order in the interface)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, IMO every "set" we have is in fact an ordered set, so specifying ordered set vs. just set is more, not less, confusing.

Am I wrong and is there in fact any unordered set? Or should we just use set and use unordered set when we don't expect you to be able to preserve order?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A set in set theory is unordered, and calling a programming data structure a set outside of this spec generally implies it to be unordered. Even if it wouldn’t be exactly wrong for the spec to only say in one place "all sets in this document are implicitly ordered" it’s not great spec-writing. Specifying "ordered set" explicitly every time makes readers less likely to make a wrong assumption.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimonSapin

A set in set theory is unordered

Veering a little off topic, but that's not technically true, more accurate might be to say that pure set theory is "order agnostic" - the concept of order is entirely absent from the definition of a set. Typically set theory and order theory go hand in hand, and a set can be ordered, unordered, or even partially ordered.

That said, I agree with your conclusion that explicit > implicit.


@mjmahone

IMO Interface/Object is not really an ordered set? Maybe it is? It feels a little bit annoying for maintainers to feel like they must maintain order (does the order of fields in the object need to match the order in the interface)?

Great catch. IMO an interface defines an ordered set of fields (order is significant, and must be reflected in introspection); however we should state that implementations can implement this set of fields in any order - I've added a commit 7170d82 to address this.

Interface types which implement this Interface are guaranteed to implement those
fields (in any order). Whenever a field claims it will return an Interface type,
it will return a valid implementing Object type during execution.

A `Union` defines a list of possible types; similar to interfaces, whenever the
A `Union` defines a set of possible types; similar to interfaces, whenever the
type system claims a union will be returned, one of the possible types will be
returned.

Expand Down Expand Up @@ -674,11 +675,11 @@ GraphQL operations are hierarchical and composed, describing a tree of
information. While Scalar types describe the leaf values of these hierarchical
operations, Objects describe the intermediate levels.

GraphQL Objects represent a list of named fields, each of which yield a value of
a specific type. Object values should be serialized as ordered maps, where the
selected field names (or aliases) are the keys and the result of evaluating the
field is the value, ordered by the order in which they appear in the _selection
set_.
GraphQL Objects represent an ordered set of named fields, each of which yield a
value of a specific type. Object values should be serialized as ordered maps,
where the selected field names (or aliases) are the keys and the result of
evaluating the field is the value, ordered by the order in which they appear in
the _selection set_.

All fields defined within an Object type must not have a name which begins with
{"\_\_"} (two underscores), as this is used exclusively by GraphQL's
Expand Down Expand Up @@ -920,7 +921,8 @@ of rules must be adhered to by every Object type in a GraphQL schema.
returns {true}.
4. If argument type is Non-Null and a default value is not defined:
1. The `@deprecated` directive must not be applied to this argument.
3. An object type may declare that it implements one or more unique interfaces.
3. An object type may declare that it implements one or more unique interfaces,
these interfaces form an ordered set.
4. An object type must be a super-set of all interfaces it implements:
1. Let this object type be {objectType}.
2. For each interface declared implemented as {interfaceType},
Expand Down Expand Up @@ -982,7 +984,7 @@ InputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]?

Object fields are conceptually functions which yield values. Occasionally object
fields can accept arguments to further specify the return value. Object field
arguments are defined as a list of all possible argument names and their
arguments are defined as an ordered set of all possible argument names and their
expected input types.
Comment on lines +987 to 988

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to fields (comment above): rather than a set, shouldn’t field definitions (and directive definitions) have an ordered map of argument names to argument definitions?

And the values of this map are more than expected types: an argument definition may also include a default value and directives)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here can be interpreted as "(an ordered set of all possible argument names) (and their expected input types)" so the set is fine; but I hate ambiguity and would rather define it as a map too. I think that's currently out of scope for this PR though.


All arguments defined within a field must not have a name which begins with
Expand Down Expand Up @@ -1093,9 +1095,10 @@ InterfaceTypeDefinition :
- Description? interface Name ImplementsInterfaces? Directives[Const]?
[lookahead != `{`]

GraphQL interfaces represent a list of named fields and their arguments. GraphQL
objects and interfaces can then implement these interfaces which requires that
the implementing type will define all fields defined by those interfaces.
GraphQL interfaces represent an ordered set of named fields and their arguments.
GraphQL objects and interfaces can then implement these interfaces which
requires that the implementing type will define all fields defined by those
interfaces.

Fields on a GraphQL interface have the same rules as fields on a GraphQL object;
their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping
Expand Down Expand Up @@ -1347,10 +1350,10 @@ UnionMemberTypes :
- UnionMemberTypes | NamedType
- = `|`? NamedType

GraphQL Unions represent an object that could be one of a list of GraphQL Object
types, but provides for no guaranteed fields between those types. They also
differ from interfaces in that Object types declare what interfaces they
implement, but are not aware of what unions contain them.
GraphQL Unions represent an object that could be one of an ordered set of
GraphQL Object types, but provides for no guaranteed fields between those types.
They also differ from interfaces in that Object types declare what interfaces
they implement, but are not aware of what unions contain them.

With interfaces and objects, only those fields defined on the type can be
queried directly; to query other fields on an interface, typed fragments must be
Expand Down Expand Up @@ -1408,7 +1411,7 @@ A valid operation includes typed fragments (in this example, inline fragments):
```

Union members may be defined with an optional leading `|` character to aid
formatting when representing a longer list of possible types:
formatting when representing a longer set of possible types:

```raw graphql example
union SearchResult =
Expand Down Expand Up @@ -1547,9 +1550,9 @@ InputFieldsDefinition : { InputValueDefinition+ }
Fields may accept arguments to configure their behavior. These inputs are often
scalars or enums, but they sometimes need to represent more complex values.

A GraphQL Input Object defines a set of input fields; the input fields are
either scalars, enums, or other input objects. This allows arguments to accept
arbitrarily complex structs.
A GraphQL Input Object defines an ordered set of named input fields; the input
fields are either scalars, enums, or other input objects. This allows arguments
to accept arbitrarily complex structs.

In this example, an Input Object called `Point2D` describes `x` and `y` inputs:

Expand Down Expand Up @@ -1936,6 +1939,10 @@ A GraphQL schema describes directives which are used to annotate various parts
of a GraphQL document as an indicator that they should be evaluated differently
by a validator, executor, or client tool such as a code generator.

Directives can accept arguments to further specify their behavior. Directive
arguments are defined as an ordered set of all possible argument names and their
expected input types.

**Built-in Directives**

:: A _built-in directive_ is any directive defined within this specification.
Expand Down Expand Up @@ -1983,7 +1990,7 @@ fragment SomeFragment on SomeType {
```

Directive locations may be defined with an optional leading `|` character to aid
formatting when representing a longer list of possible locations:
formatting when representing a longer set of possible locations:

```raw graphql example
directive @example on
Expand Down
Loading