Skip to content

Question mark sn #3

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

Open
wants to merge 5 commits into
base: semantic-non-null
Choose a base branch
from
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion spec/Appendix B -- Grammar Summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Token ::
- FloatValue
- StringValue

Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | }
Punctuator :: one of ! ? $ & ( ) ... : = @ [ ] { | }

Name ::

Expand Down
56 changes: 54 additions & 2 deletions spec/Section 2 -- Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,8 @@ that variable, that operation is invalid (see

## Type References

### In traditional nullability mode

Type :

- NamedType
Expand All @@ -1239,10 +1241,49 @@ NonNullType :
- NamedType !
- ListType !

**Semantics**

Type : Name

- Let {name} be the string value of {Name}
- Let {type} be the type defined in the Schema named {name}
- {type} must not be {null}
- Return {type}

Type : [ Type ]

- Let {itemType} be the result of evaluating {Type}
- Let {type} be a List type where {itemType} is the contained type.
- Return {type}

Type : Type !

- Let {nullableType} be the result of evaluating {Type}
- Let {type} be a Non-Null type where {nullableType} is the contained type.
- Return {type}

### In semantic nullability mode

Type :

- NamedType
- ListType
- NonNullType
- SemanticNonNullType

NamedType : Name ?

ListType : [ Type ]

NonNullType :

- Name !
- ListType !

SemanticNonNullType :

- ! NamedType
- ! ListType
- Name [lookahead != `?`] [lookahead != `!`]
- ListType [lookahead != `?`] [lookahead != `!`]

GraphQL describes the types of data expected by arguments and variables. Input
types may be lists of another input type, or a non-null variant of any other
Expand All @@ -1257,6 +1298,17 @@ Type : Name
- {type} must not be {null}
- Return {type}
Copy link
Owner

Choose a reason for hiding this comment

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

I think you want to delete this semantic block


Type : Type ?

- Let {nullableType} be the result of evaluating {Type}
- Return {nullableType}
Comment on lines +1301 to +1304
Copy link
Owner

Choose a reason for hiding this comment

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

This is really confusing; I think delete the Type : Type block above, and instead have:

Suggested change
Type : Type ?
- Let {nullableType} be the result of evaluating {Type}
- Return {nullableType}
Type : Name ?
- Let {name} be the string value of {Name}
- Let {type} be the type defined in the Schema named {name}
- {type} must not be {null}
- Return {type}
Type : ListType ?
- Let {itemType} be the result of evaluating {ListType}
- Let {type} be a List type where {itemType} is the contained type.
- Return {type}


Type : Type [lookahead != `?`] [lookahead != `!`]

- Let {nullableType} be the result of evaluating {Type}
- Let {type} be a Semantically-Non-Null type where {nullableType} is the contained type.
- Return {type}

Type : [ Type ]

- Let {itemType} be the result of evaluating {Type}
Expand Down
79 changes: 47 additions & 32 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -1905,18 +1905,17 @@ Following are examples of result coercion with various types and values:
The GraphQL Semantic-Non-Null type is an alternative to the GraphQL Non-Null
type to disallow null unless accompanied by a field error. This type wraps an
underlying type, and this type acts identically to that wrapped type, with the
exception that {null} will result in a field error being raised. A leading
exclamation mark is used to denote a field that uses a Semantic-Non-Null type
like this: `name: !String`.

exception that {null} will result in a field error being raised. Semantic-Non-Null
types are only valid in a document that also contains a `@SemanticNullability`
document-level directive. In such a document, Semantic-Non-Null fields have no
adornment, much like nullable fields in documents without that directive.
Comment on lines +1908 to +1911
Copy link
Owner

Choose a reason for hiding this comment

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

The type system should be valid without reference to documents; support for SDL is optional.

For example: `name: String`.

In docuements with a `@SemanticNullability` directive, input types are unaltered in
terms of syntax. Unadorned types still represent nullable input types.
Comment on lines +1914 to +1915
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
In docuements with a `@SemanticNullability` directive, input types are unaltered in
terms of syntax. Unadorned types still represent nullable input types.
In documents with a `@SemanticNullability` directive, input types are unaltered in
terms of syntax. Unadorned types still represent nullable input types.

Copy link
Owner

Choose a reason for hiding this comment

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

Wait so unadorned is nullable on input but non-null on output? What about if we add something like struct in future that allows the same composite type to be represented on both input and output - you're saying that the input field is allowed to be null, but when it's presented through output it would cause a non-null error to be raised?

@semanticNullability

struct Struct {
  nullable: Int?
  semanticNonNull: Int
  strictNonNull: Int!
}
type Query {
  identity(struct: Struct): Struct
}

query {
  # Valid input since unadorned is nullable on input?
  identity(struct: { nullable: null, semanticNonNull: null, strictNonNull: 0 }) {
    nullable
    # Identity produces error here?
    semanticNonNull
    strictNonNull
  }
}

Semantic-Non-Null types are only valid for use as an _output type_; they must
not be used as an _input type_.

**Nullable vs. Optional**

Fields that return Semantic-Non-Null types will never return the value {null} if
queried _unless_ an error has been logged for that field.

**Result Coercion**

To coerce the result of a Semantic-Non-Null type, the coercion of the wrapped
Expand Down Expand Up @@ -1947,35 +1946,35 @@ complex types. The rules for result coercion of Lists and Semantic-Non-Null
types apply in a recursive fashion.

For example if the inner item type of a List is Semantic-Non-Null (e.g. `[!T]`),
then that List may not contain any {null} items unless associated field errors
were raised. However if the inner type of a Semantic-Non-Null is a List (e.g.
then if that list contains any nulls without associated errors, then field errors will be raised.
However if the inner type of a Semantic-Non-Null is a List (e.g.
`![T]`), then {null} is not accepted without an accompanying field error being
raised, however an empty list is accepted.

Following are examples of result coercion with various types and values:

| Expected Type | Internal Value | Coerced Result |
| ------------- | --------------- | ------------------------------------------- |
| `![Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `![Int]` | `null` | `null` (With logged coercion error) |
| `![Int]` | `[1, 2, null]` | `[1, 2, null]` |
| `![Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `![Int!]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `![Int!]` | `null` | `null` (With logged coercion error) |
| `![Int!]` | `[1, 2, null]` | `null` (With logged coercion error) |
| `![Int!]` | `[1, 2, Error]` | `null` (With logged error) |
| `[!Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[!Int]` | `null` | `null` |
| `[!Int]` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `[!Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `[!Int]!` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[!Int]!` | `null` | Error: Value cannot be null |
| `[!Int]!` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `[!Int]!` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `![!Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `![!Int]` | `null` | `null` (With logged coercion error) |
| `![!Int]` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `![!Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `[Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[Int]` | `null` | `null` (With logged coercion error) |
| `[Int]` | `[1, 2, null]` | `[1, 2, null]` |
| `[Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `[Int!]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[Int!]` | `null` | `null` (With logged coercion error) |
| `[Int!]` | `[1, 2, null]` | `null` (With logged coercion error) |
| `[Int!]` | `[1, 2, Error]` | `null` (With logged error) |
| `[Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[Int]` | `null` | `null` |
| `[Int]` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `[Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `[Int]!` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[Int]!` | `null` | Error: Value cannot be null |
| `[Int]!` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `[Int]!` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |
| `[Int]` | `[1, 2, 3]` | `[1, 2, 3]` |
| `[Int]` | `null` | `null` (With logged coercion error) |
| `[Int]` | `[1, 2, null]` | `[1, 2, null]` (With logged coercion error) |
| `[Int]` | `[1, 2, Error]` | `[1, 2, null]` (With logged error) |

## Directives

Expand Down Expand Up @@ -2016,6 +2015,7 @@ TypeSystemDirectiveLocation : one of
- `ENUM_VALUE`
- `INPUT_OBJECT`
- `INPUT_FIELD_DEFINITION`
- `DOCUMENT`

Choose a reason for hiding this comment

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

Should we extract document level directives to a separate spec PR, it seems to be lumped in here but sounds like a meaningful addition in the grander scheme either way

Copy link
Author

Choose a reason for hiding this comment

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

Yeah truthfully we could also do no null propagation mode as it's own PR as well.

Copy link
Owner

Choose a reason for hiding this comment

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

These should indeed be separate PRs 👍


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
Expand All @@ -2025,7 +2025,8 @@ by a validator, executor, or client tool such as a code generator.

:: A _built-in directive_ is any directive defined within this specification.

GraphQL implementations should provide the `@skip` and `@include` directives.
GraphQL implementations should provide the `@skip`, `@include`, and
`@semanticNullability` directives.

GraphQL implementations that support the type system definition language must
provide the `@deprecated` directive if representing deprecated portions of the
Expand Down Expand Up @@ -2180,6 +2181,20 @@ the `@include` condition is true. Stated conversely, the field or fragment must
_not_ be queried if either the `@skip` condition is true _or_ the `@include`
condition is false.

### @semanticNullability

```graphql
directive @semanticNullability on DOCUMENT
```

The `@semanticNullability` _built-in directive_ may be provided for documents,
and causes GraphQL to interpret nullability in that document based on the rules
for Semantic Nullability. In that document an unadorned named type will be treated
as if it is wrapped with the Semantic-Non-Null type. Any fields adorned with a question
mark ie `name: Type?` will be treated as a named nullable type.

An unadorned input type will be treated as a nullable type.

### @deprecated

```graphql
Expand Down
16 changes: 11 additions & 5 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,16 @@ enum __TypeKind {
SEMANTIC_NON_NULL
}

enum __NullabilityMode {
TRADITIONAL
FULL
}

type __Field {
name: String!
description: String
args(includeDeprecated: Boolean = false): [__InputValue!]!
type(includeSemanticNonNull: Boolean! = false): __Type!
type(nullabilityMode: __NullabilityMode! = __NullabilityMode.TRADITIONAL): __Type!
isDeprecated: Boolean!
deprecationReason: String
}
Expand Down Expand Up @@ -416,8 +421,9 @@ Fields\:
GraphQL types are nullable. The value {null} is a valid response for field type.

A Semantic-Non-Null type is a type modifier: it wraps another _output type_
instance in the `ofType` field. Semantic-Non-Null types do not allow {null} as a
response _unless_ an associated _field error_ has been raised.
instance in the `ofType` field. Semantic-Non-Null types allow {null} as a
response, however if an associated _field error_ has not already been raised,
one will be produced.

The modified type in the `ofType` field may itself be a modified List type,
allowing the representation of Semantic-Non-Null of Lists. However it must not
Expand Down Expand Up @@ -445,8 +451,8 @@ Fields\:
{true}, deprecated arguments are also returned.
- `type` must return a `__Type` that represents the type of value returned by
this field.
- Accepts the argument `includeSemanticNonNull` which defaults to {false}. If
{false}, let {fieldType} be the type of value returned by this field and
- Accepts the argument `nullabilityMode` which defaults to {__NullabilityMode.TRADITIONAL}. If
{__NullabilityMode.TRADITIONAL}, let {fieldType} be the type of value returned by this field and
instead return a `__Type` that represents
{RecursivelyStripSemanticNonNullTypes(fieldType)}.
- `isDeprecated` returns {true} if this field should no longer be used,
Expand Down
4 changes: 2 additions & 2 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,8 +784,8 @@ response.

If the result of resolving a field is {null} (either because the function to
resolve the field returned {null} or because a field error was raised), and that
field is of a `Non-Null` type, then a field error is raised. The error must be
added to the {"errors"} list in the response.
field is of a `Non-Null` or `Semantically-Non-Null` type, then a field error is
raised. The error must be added to the {"errors"} list in the response.

If the field returns {null} because of a field error which has already been
added to the {"errors"} list in the response, the {"errors"} list must not be
Expand Down