From 893b04a4294b75e865b79b5c1e562b63665b9625 Mon Sep 17 00:00:00 2001 From: Matt Mahoney Date: Fri, 15 May 2026 17:43:55 -0400 Subject: [PATCH 1/3] Base GAP proposal for Set-semantic compatible Type System Definition syntax --- gaps/GAP-33/DRAFT.md | 305 +++++++++++++++++++++++++++++++++++++++ gaps/GAP-33/README.md | 60 ++++++++ gaps/GAP-33/metadata.yml | 12 ++ 3 files changed, 377 insertions(+) create mode 100644 gaps/GAP-33/DRAFT.md create mode 100644 gaps/GAP-33/README.md create mode 100644 gaps/GAP-33/metadata.yml diff --git a/gaps/GAP-33/DRAFT.md b/gaps/GAP-33/DRAFT.md new file mode 100644 index 0000000..f75fbf3 --- /dev/null +++ b/gaps/GAP-33/DRAFT.md @@ -0,0 +1,305 @@ +# GraphQL Schema Definition Language - Set Extensions + +This document specifics an alternative to the [Type System Document](https://spec.graphql.org/draft/#sec-Type-System) for defining a set-compatible version of the type system of a GraphQL Schema. + +It is common to need to treat schema documents as sets: we may want +to merge two documents to create a "Composite Schema", or you might +want to know the intersection between documents to build +products that work against all the provided GraphQL Services. + +Any grammar not defined, but used, in this document is a reference +to the grammar defined in [the GraphQL Specification's Grammar](https://spec.graphql.org/draft/#sec-Appendix-Grammar-Summary). + +The key change to the grammar: **every location in the schema that can have a directive now can also be an Extension**. + +To put it another way, if a location in the Schema can be defined by a +[Schema Coordinate](https://spec.graphql.org/draft/#sec-Schema-Coordinates), then it can exist in the Set Type System in an `extend` representation. Additionally, the root `schema` has an `extend` representation, because it can apply set operations. + +# Set Type System + +SetTypeSystemDocument : SetTypeSystemDefinitionOrExtension+ + +TypeSystemDefinition : + +- SetSchemaDefinition +- SetTypeDefinition +- SetDirectiveDefinition + +SetTypeSystemDefinitionOrExtension : + +- SetTypeSystemDefinition +- SetTypeSystemExtension + +SetTypeSystemExtension : + +- SetSchemaExtension +- SetTypeExtension +- SetDirectiveExtension + +## Schema + +SetSchemaDefinition : Description? schema Directives[Const]? { +RootOperationTypeDefinition+ } + +RootOperationTypeDefinition : OperationType : NamedType + +### Schema Extension + +SetSchemaExtension : + +- extend schema Directives[Const]? { RootOperationTypeDefinition+ } +- extend schema Directives[Const] [lookahead != `{`] + +## Types + +SetTypeDefinition : + +- SetScalarTypeDefinition +- SetObjectTypeDefinition +- SetInterfaceTypeDefinition +- SetUnionTypeDefinition +- SetEnumTypeDefinition +- SetInputObjectTypeDefinition + +### Type Extensions + +SetTypeExtension : + +- SetScalarTypeExtension +- SetObjectTypeExtension +- SetInterfaceTypeExtension +- SetUnionTypeExtension +- SetEnumTypeExtension +- SetInputObjectTypeExtension + +## Scalars + +SetScalarTypeDefinition : Description? scalar Name Directives[Const]? + +### Scalar Extensions + +SetScalarTypeExtension : + +- extend scalar Name Directives[Const] + +## Objects + +SetObjectTypeDefinition : + +- Description? type Name ImplementsInterfaces? Directives[Const]? + SetFieldsDefinitionOrExtension +- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != + `{`] + +SetObjectTypeExtension : + +- extend type Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend type Name ImplementsInterfaces [lookahead != `{`] + +ImplementsInterfaces : + +- ImplementsInterfaces & NamedType +- implements `&`? NamedType + +SetFieldsDefinitionOrExtension : { SetFieldDefinitionOrExtension+ } + +## Fields + +SetFieldDefinitionOrExtension: + +- SetFieldDefinition +- SetFieldExtension + +SetFieldDefinition : Description? Name SetArgumentsDefinitionOrExtension? : Type +Directives[Const]? + +SetFieldExtension : extend Name SetArgumentsDefinitionOrExtension? Directives[Const]? + +Field extensions are different from fields defined by a type extension. The below is valid syntax: + +```graphql field-extension +extend type Person { + name: String + extend age @deprecated +} + +type Business { + extend name @deprecated +} +``` +To become a valid GraphQL Spec document, the above would need to +be `union`'d with a document like: + +```graphql field-extension-merge +type Person { + age: Int + extend name @deprecated +} + +extend type Business { + name: String +} +``` + +resulting in: + +```graphql field-extension-union +type Person { + age: Int @deprecated + name: String @deprecated +} + +type Business { + name: String @deprecated +} +``` +which is now a valid GraphQL Type System Document. + +### Field Arguments + +SetArgumentsDefinition : ( SetInputValueDefinitionOrExtension+ ) + +SetInputValueDefinitionOrExtension : +- SetInputValueDefinition +- SetInputValueExtension + +SetInputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]? + +SetInputValueExtension : extend Name Directives[Const]? + +Similar to SetFieldExtension, SetInputValueExtension cannot define the input value's type, and we don't know a good representation that would allow us to add a DefaultValue to the input. + +Example: +```graphql argument-extension +type Person { + name: String + extend age(minimum: Int) +} +``` +union +```graphql argument-extension-union +type Person { + extend name(short: Boolean) + age(extend minimum @deprecated): Int +} +``` +results in: +``` +type Person { + name(short: Boolean): String + age(minimum @deprecated): Int +} +``` + +SetInterfaceTypeDefinition : + +- Description? interface Name ImplementsInterfaces? Directives[Const]? + SetFieldsDefinitionOrExtension +- Description? interface Name ImplementsInterfaces? Directives[Const]? + [lookahead != `{`] + +SetInterfaceTypeExtension : + +- extend interface Name ImplementsInterfaces? Directives[Const]? + SetFieldsDefinitionOrExtension +- extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != + `{`] +- extend interface Name ImplementsInterfaces [lookahead != `{`] + +## Unions + +SetUnionTypeDefinition : Description? union Name Directives[Const]? +UnionMemberTypes? + +UnionMemberTypes : + +- UnionMemberTypes | NamedType +- = `|`? NamedType + +SetUnionTypeExtension : + +- extend union Name Directives[Const]? UnionMemberTypes +- extend union Name Directives[Const] + +NOTE: SetUnionTypeDefinition to UnionTypeDefinition, and SetUnionTypeExtension is equivalent to UnionTypeExtension. This is because Union does not have any possible inner Schema Coordinates (union members are *not* possible coordinates). + +## Enums + +SetEnumTypeDefinition : + +- Description? enum Name Directives[Const]? SetEnumValuesDefinitionOrExtension +- Description? enum Name Directives[Const]? [lookahead != `{`] + +SetEnumTypeExtension : + +- extend enum Name Directives[Const]? SetEnumValuesDefinitionOrExtension +- extend enum Name Directives[Const] [lookahead != `{`] + +SetEnumValuesDefinitionOrExtension : { SetEnumValueDefinitionOrExtension+ } + +SetEnumValueDefinitionOrExtension : +- SetEnumValueDefinition +- SetEnumValueExtension + +SetEnumValueDefinition : Description? EnumValue Directives[Const]? +SetEnumValueExtension : extend EnumValue Directives[Const]? + +## Input Objects + +InputObjectTypeDefinition : + +- Description? input Name Directives[Const]? SetInputFieldsDefinitionOrExtension +- Description? input Name Directives[Const]? [lookahead != `{`] + +InputObjectTypeExtension : + +- extend input Name Directives[Const]? SetInputFieldsDefinitionOrExtension +- extend input Name Directives[Const] [lookahead != `{`] + +SetInputFieldsDefinition : { SetInputValueDefinitionOrExtension+ } + +## Directives + +NOTE: we are assuming that directives on directive definitions, (https://github.com/graphql/graphql-spec/pull/1206) is in the spec at this point. + +SetDirectiveDefinition : Description? directive @ Name SetArgumentsDefinition? Directives[Const]? +`repeatable`? on DirectiveLocations + +SetDirectiveExtension : extend directive @ Name SetArgumentsDefinition? Directives[Const] + +DirectiveLocations : + +- DirectiveLocations | DirectiveLocation +- `|`? DirectiveLocation + +DirectiveLocation : + +- ExecutableDirectiveLocation +- TypeSystemDirectiveLocation + +ExecutableDirectiveLocation : one of + +- `QUERY` +- `MUTATION` +- `SUBSCRIPTION` +- `FIELD` +- `FRAGMENT_DEFINITION` +- `FRAGMENT_SPREAD` +- `INLINE_FRAGMENT` +- `VARIABLE_DEFINITION` + +TypeSystemDirectiveLocation : one of + +- `SCHEMA` +- `SCALAR` +- `OBJECT` +- `FIELD_DEFINITION` +- `ARGUMENT_DEFINITION` +- `INTERFACE` +- `UNION` +- `ENUM` +- `ENUM_VALUE` +- `INPUT_OBJECT` +- `INPUT_FIELD_DEFINITION` +- `DIRECTIVE_DEFINITION` diff --git a/gaps/GAP-33/README.md b/gaps/GAP-33/README.md new file mode 100644 index 0000000..0fbd02f --- /dev/null +++ b/gaps/GAP-33/README.md @@ -0,0 +1,60 @@ +# GAP-33: GraphQL Schema Definition Language - Set Extensions + +## Overview + +This proposal adds syntax to the [GraphQL Type System](https://spec.graphql.org/draft/#sec-Type-System)'s IDL +(sometimes called the "Schema Definition Language" or SDL) to allow for a *syntax* representation +that support set *semantics*. This GAP does not yet define the *behavior* of set operations. + +Additionally, a Document using these set-extended semantics is *not* considered a +GraphQL Type System Document, unless it would parse, validly, using the Spec standard Type System syntax. + +This is a potential alternative to https://github.com/graphql/gaps/pull/4. + +## Motivation + +It is common for tooling working with GraphQL to need to compose sets. This can be defined as a "union" operation. +For instance, composite schemas need a merge operation, as defined in the [Composite Schemas Spec](https://graphql.github.io/composite-schemas-spec/draft/#sec-Merge). + +Another need is to partially *intersect* schemas. For instance, if we want to know the schema that +is common across two different base schemas, an intersection lets us build products that will work against either. + +Another common requirement we've run into is determining whether a schema that an older client builds against +is a *true subset* of the current server schema. We can determine this by using an *exclude* operation. + +However, the Type System Document syntax grammar does not support all of these operations well. For instance, +how do you represent +```graphql +type A { + a(arg: Int): Int +} + +type B { + b: Int @something +} +``` +**exclude** +```graphql +type A { + a: Int +} + +extend type B { + b: Int +} +``` + +The closest we could get in the current Spec syntax is: +```graphql +extend type A { + a(arg: Int): Int +} + +type B { + b: Int @something +} +``` +Notice that we *do* have syntax, via the `extend` keyword, to showcase that `type A` is excluded from the exclude-set. +But we don't have any syntax to show that `A.a` is excluded but `A.a(arg:)` is not. + +Likewise we can show that `type B` is *not* excluded, but we have no way of showing that the field, `B.b`, IS excluded, but the directive, `@something`, on `B.b` is NOT excluded. diff --git a/gaps/GAP-33/metadata.yml b/gaps/GAP-33/metadata.yml new file mode 100644 index 0000000..22e3a48 --- /dev/null +++ b/gaps/GAP-33/metadata.yml @@ -0,0 +1,12 @@ +id: 33 +title: GraphQL Schema Definition Language - Set Extensions +summary: > + An auxiliary syntax extending the Schema Definition Language syntax, + unlocking set-like operations (union, exclude, intersect) on SDL documents. +status: proposal +authors: + - name: "Matt Mahoney" + email: "mahoney.mattj@gmail.com" + githubUsername: "@mjmahone" +sponsor: "@mjmahone" +discussion: "https://github.com/graphql/gaps/pull/33" From f8b3fbca0caf5c573a3737b6170e8b4387ed602b Mon Sep 17 00:00:00 2001 From: Matt Mahoney Date: Fri, 15 May 2026 19:22:01 -0400 Subject: [PATCH 2/3] Allow extend fields to define type or not, same with args --- gaps/GAP-33/DRAFT.md | 295 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 264 insertions(+), 31 deletions(-) diff --git a/gaps/GAP-33/DRAFT.md b/gaps/GAP-33/DRAFT.md index f75fbf3..9d27c03 100644 --- a/gaps/GAP-33/DRAFT.md +++ b/gaps/GAP-33/DRAFT.md @@ -19,17 +19,17 @@ To put it another way, if a location in the Schema can be defined by a SetTypeSystemDocument : SetTypeSystemDefinitionOrExtension+ -TypeSystemDefinition : - -- SetSchemaDefinition -- SetTypeDefinition -- SetDirectiveDefinition - SetTypeSystemDefinitionOrExtension : - SetTypeSystemDefinition - SetTypeSystemExtension +SetTypeSystemDefinition : + +- SetSchemaDefinition +- SetTypeDefinition +- SetDirectiveDefinition + SetTypeSystemExtension : - SetSchemaExtension @@ -50,6 +50,8 @@ SetSchemaExtension : - extend schema Directives[Const]? { RootOperationTypeDefinition+ } - extend schema Directives[Const] [lookahead != `{`] +SetSchemaDefinition and SetSchemaExtension are grammatically identical to the [Schema](https://spec.graphql.org/draft/#sec-Schema) in the GraphQL Specification. + ## Types SetTypeDefinition : @@ -76,6 +78,8 @@ SetTypeExtension : SetScalarTypeDefinition : Description? scalar Name Directives[Const]? +SetScalarTypeDefinition and SetScalarTypeExtension are gramatically identical to the [Scalars](https://spec.graphql.org/draft/#sec-Scalars) Specification. + ### Scalar Extensions SetScalarTypeExtension : @@ -91,12 +95,6 @@ SetObjectTypeDefinition : - Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] -SetObjectTypeExtension : - -- extend type Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension -- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] -- extend type Name ImplementsInterfaces [lookahead != `{`] - ImplementsInterfaces : - ImplementsInterfaces & NamedType @@ -104,6 +102,14 @@ ImplementsInterfaces : SetFieldsDefinitionOrExtension : { SetFieldDefinitionOrExtension+ } +### Object Extensions + +SetObjectTypeExtension : + +- extend type Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend type Name ImplementsInterfaces [lookahead != `{`] + ## Fields SetFieldDefinitionOrExtension: @@ -111,17 +117,27 @@ SetFieldDefinitionOrExtension: - SetFieldDefinition - SetFieldExtension -SetFieldDefinition : Description? Name SetArgumentsDefinitionOrExtension? : Type -Directives[Const]? +SetFieldDefinition : +- Description? Name SetArgumentsDefinitionOrExtension? : Type Directives[Const]? +- Description? Name SetArgumentsDefinitionOrExtension [lookahead != `:`] Directives[Const]? +- Description? Name [lookahead != `:`] Directives[Const]? + +### Field Extensions -SetFieldExtension : extend Name SetArgumentsDefinitionOrExtension? Directives[Const]? +SetFieldExtension : +- extend Name SetArgumentsDefinitionOrExtension? : Type Directives[Const]? +- extend Name SetArgumentsDefinitionOrExtension [lookahead != `:`] Directives[Const]? +- extend Name [lookahead != `:`] Directives[Const]? + +It is possible for the type definition of a field to only be present in that field's extension. Likewise, we need to be able to extend a field (like deprecating it) +with a guarantee that the field's type will not change through the extension. Field extensions are different from fields defined by a type extension. The below is valid syntax: ```graphql field-extension extend type Person { - name: String extend age @deprecated + name } type Business { @@ -134,7 +150,7 @@ be `union`'d with a document like: ```graphql field-extension-merge type Person { age: Int - extend name @deprecated + extend name: String @deprecated } extend type Business { @@ -156,6 +172,11 @@ type Business { ``` which is now a valid GraphQL Type System Document. +The syntax of allowing field definitions without a type definition +enables tooling, such as diff tools, to have a valid *syntax* for +how a schema is evolving, even if the *semantic meaning* of the syntax +would not produce a valid GraphQL Schema. + ### Field Arguments SetArgumentsDefinition : ( SetInputValueDefinitionOrExtension+ ) @@ -164,23 +185,33 @@ SetInputValueDefinitionOrExtension : - SetInputValueDefinition - SetInputValueExtension -SetInputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]? +### Input Values + +SetInputValueDefinition : + +- Description? Name : Type DefaultValue? Directives[Const]? +- Description? Name [lookahead != `:`] DefaultValue? Directives[Const]? + +### Input Value Extensions -SetInputValueExtension : extend Name Directives[Const]? +SetInputValueExtension : -Similar to SetFieldExtension, SetInputValueExtension cannot define the input value's type, and we don't know a good representation that would allow us to add a DefaultValue to the input. +- extend Name : Type Directives[Const]? +- extend Name [lookahead != `:`] Directives[Const]? + +Similar to Field Definitions and Extensions, Input Value Definitions and Extensions cannot may elide the input value's type. Example: ```graphql argument-extension type Person { - name: String + name(short): String extend age(minimum: Int) } ``` union ```graphql argument-extension-union type Person { - extend name(short: Boolean) + extend name(extend short: Boolean) age(extend minimum @deprecated): Int } ``` @@ -217,12 +248,14 @@ UnionMemberTypes : - UnionMemberTypes | NamedType - = `|`? NamedType +### Union Extensions + SetUnionTypeExtension : - extend union Name Directives[Const]? UnionMemberTypes - extend union Name Directives[Const] -NOTE: SetUnionTypeDefinition to UnionTypeDefinition, and SetUnionTypeExtension is equivalent to UnionTypeExtension. This is because Union does not have any possible inner Schema Coordinates (union members are *not* possible coordinates). +SetUntionTypeExtension is gramatically identical to the [Unions](https://spec.graphql.org/draft/#sec-Unions) in the GraphQL Specification. ## Enums @@ -231,10 +264,7 @@ SetEnumTypeDefinition : - Description? enum Name Directives[Const]? SetEnumValuesDefinitionOrExtension - Description? enum Name Directives[Const]? [lookahead != `{`] -SetEnumTypeExtension : - -- extend enum Name Directives[Const]? SetEnumValuesDefinitionOrExtension -- extend enum Name Directives[Const] [lookahead != `{`] +### Enum Values SetEnumValuesDefinitionOrExtension : { SetEnumValueDefinitionOrExtension+ } @@ -245,6 +275,13 @@ SetEnumValueDefinitionOrExtension : SetEnumValueDefinition : Description? EnumValue Directives[Const]? SetEnumValueExtension : extend EnumValue Directives[Const]? +### Enum Extensions + +SetEnumTypeExtension : + +- extend enum Name Directives[Const]? SetEnumValuesDefinitionOrExtension +- extend enum Name Directives[Const] [lookahead != `{`] + ## Input Objects InputObjectTypeDefinition : @@ -252,21 +289,215 @@ InputObjectTypeDefinition : - Description? input Name Directives[Const]? SetInputFieldsDefinitionOrExtension - Description? input Name Directives[Const]? [lookahead != `{`] +SetInputFieldsDefinitionOrExtension : { SetInputValueDefinitionOrExtension+ } + +## Input Object Extensions + InputObjectTypeExtension : - extend input Name Directives[Const]? SetInputFieldsDefinitionOrExtension - extend input Name Directives[Const] [lookahead != `{`] -SetInputFieldsDefinition : { SetInputValueDefinitionOrExtension+ } - ## Directives NOTE: we are assuming that directives on directive definitions, (https://github.com/graphql/graphql-spec/pull/1206) is in the spec at this point. -SetDirectiveDefinition : Description? directive @ Name SetArgumentsDefinition? Directives[Const]? +SetDirectiveDefinition : Description? directive @ Name SetArgumentsDefinitionOrExtension? Directives[Const]? `repeatable`? on DirectiveLocations -SetDirectiveExtension : extend directive @ Name SetArgumentsDefinition? Directives[Const] +DirectiveLocations : + +- DirectiveLocations | DirectiveLocation +- `|`? DirectiveLocation + +DirectiveLocation : + +- ExecutableDirectiveLocation +- TypeSystemDirectiveLocation + +ExecutableDirectiveLocation : one of + +- `QUERY` +- `MUTATION` +- `SUBSCRIPTION` +- `FIELD` +- `FRAGMENT_DEFINITION` +- `FRAGMENT_SPREAD` +- `INLINE_FRAGMENT` +- `VARIABLE_DEFINITION` + +TypeSystemDirectiveLocation : one of + +- `SCHEMA` +- `SCALAR` +- `OBJECT` +- `FIELD_DEFINITION` +- `ARGUMENT_DEFINITION` +- `INTERFACE` +- `UNION` +- `ENUM` +- `ENUM_VALUE` +- `INPUT_OBJECT` +- `INPUT_FIELD_DEFINITION` +- `DIRECTIVE_DEFINITION` + +### Directive Extensions + +SetDirectiveExtension : extend directive @ Name SetArgumentsDefinitionOrExtension? Directives[Const] + + +# Appendix: Grammar Summary + +SetTypeSystemExtensionDocument : SetTypeSystemDefinitionOrExtension+ + +SetTypeSystemDefinitionOrExtension : + +- SetTypeSystemDefinition +- SetTypeSystemExtension + +SetTypeSystemExtension : + +- SetSchemaExtension +- SetTypeExtension + +SetSchemaDefinition : Description? schema Directives[Const]? { +RootOperationTypeDefinition+ } + +SetSchemaExtension : + +- extend schema Directives[Const]? { RootOperationTypeDefinition+ } +- extend schema Directives[Const] [lookahead != `{`] + +RootOperationTypeDefinition : OperationType : NamedType + +SetTypeDefinition : + +- SetScalarTypeDefinition +- SetObjectTypeDefinition +- SetInterfaceTypeDefinition +- SetUnionTypeDefinition +- SetEnumTypeDefinition +- SetInputObjectTypeDefinition + +SetTypeExtension : + +- SetScalarTypeExtension +- SetObjectTypeExtension +- SetInterfaceTypeExtension +- SetUnionTypeExtension +- SetEnumTypeExtension +- SetInputObjectTypeExtension + +SetScalarTypeDefinition : Description? scalar Name Directives[Const]? + +SetScalarTypeExtension : + +- extend scalar Name Directives[Const] + +SetObjectTypeDefinition : + +- Description? type Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +SetObjectTypeExtension : + +- extend type Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend type Name ImplementsInterfaces [lookahead != `{`] + +ImplementsInterfaces : + +- ImplementsInterfaces & NamedType +- implements `&`? NamedType + +SetFieldsDefinitionOrExtension : { SetFieldDefinitionOrExtension+ } + +SetFieldDefinitionOrExtension : +- SetFieldDefinition +- SetFieldExtension + +SetFieldDefinition : +- Description? Name SetArgumentsDefinitionOrExtension? : Type Directives[Const]? +- Description? Name SetArgumentsDefinitionOrExtension [lookahead != `:`] Directives[Const]? +- Description? Name [lookahead != `:`] Directives[Const]? + +SetFieldExtension : +- extend Name SetArgumentsDefinitionOrExtension? : Type Directives[Const]? +- extend Name SetArgumentsDefinitionOrExtension [lookahead != `:`] Directives[Const]? +- extend Name [lookahead != `:`] Directives[Const]? + +SetArgumentsDefinitionOrExtension : ( SetInputValueDefinitionOrExtension+ ) + +SetInputValueDefinitionOrExtension : +- SetInputValueDefinition +- SetInputValueExtension + +SetInputValueDefinition : +- Description? Name : Type DefaultValue? Directives[Const]? +- Description? Name [lookahead != `:`] Directives[Const]? + +SetInputValueExtension : +- extend Name : Type DefaultValue? Directives[Const]? +- extend Name [lookahead != `:`] Directives[Const]? + +SetInterfaceTypeDefinition : + +- Description? interface Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +SetInterfaceTypeExtension : + +- extend interface Name ImplementsInterfaces? Directives[Const]? SetFieldsDefinitionOrExtension +- extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend interface Name ImplementsInterfaces [lookahead != `{`] + +SetUnionTypeDefinition : Description? union Name Directives[Const]? +UnionMemberTypes? + +UnionMemberTypes : + +- UnionMemberTypes | NamedType +- = `|`? NamedType + +SetUnionTypeExtension : + +- extend union Name Directives[Const]? UnionMemberTypes +- extend union Name Directives[Const] + +SetEnumTypeDefinition : + +- Description? enum Name Directives[Const]? SetEnumValuesDefinitionsOrExtension +- Description? enum Name Directives[Const]? [lookahead != `{`] + +SetEnumValuesDefinitionOrExtension : { SetEnumValueDefinitionOrExtension+ } + +SetEnumValueDefinitionOrExtension: +- SetEnumValueDefinition +- SetEnumValueExtension + +SetEnumValueDefinition : Description? EnumValue Directives[Const]? + +SetEnumValueExtension : extend EnumValue Directives[Const]? + +SetEnumTypeExtension : + +- extend enum Name Directives[Const]? SetEnumValuesDefinitionsOrExtension +- extend enum Name Directives[Const] [lookahead != `{`] + +SetInputObjectTypeDefinition : + +- Description? input Name Directives[Const]? SetInputFieldsDefinition +- Description? input Name Directives[Const]? [lookahead != `{`] + +SetInputFieldsDefinition : { SetInputFieldsDefinitionOrExtension+ } + +SetInputObjectTypeExtension : + +- extend input Name Directives[Const]? SetInputFieldsDefinition +- extend input Name Directives[Const] [lookahead != `{`] + +SetDirectiveDefinition : Description? directive @ Name SetArgumentsDefinitionOrExtension? +`repeatable`? on DirectiveLocations DirectiveLocations : @@ -303,3 +534,5 @@ TypeSystemDirectiveLocation : one of - `INPUT_OBJECT` - `INPUT_FIELD_DEFINITION` - `DIRECTIVE_DEFINITION` + +SetDirectiveExtension : extend directive @ Name SetArgumentsDefinitionOrExtension? Directives[Const] From e9b2ce89ab69704f28aa6e7f4c80792afa003999 Mon Sep 17 00:00:00 2001 From: Matt Mahoney Date: Tue, 19 May 2026 10:48:13 -0700 Subject: [PATCH 3/3] change title to not use GraphQL explicitly. Update to add some examples, will need to make more robust later --- gaps/GAP-33/DRAFT.md | 51 +++++++++++++++++++++++++++++++++++++++- gaps/GAP-33/metadata.yml | 8 +++---- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/gaps/GAP-33/DRAFT.md b/gaps/GAP-33/DRAFT.md index 9d27c03..e5c22aa 100644 --- a/gaps/GAP-33/DRAFT.md +++ b/gaps/GAP-33/DRAFT.md @@ -1,4 +1,4 @@ -# GraphQL Schema Definition Language - Set Extensions +# Set Extensions for Type System Documents This document specifics an alternative to the [Type System Document](https://spec.graphql.org/draft/#sec-Type-System) for defining a set-compatible version of the type system of a GraphQL Schema. @@ -345,6 +345,55 @@ TypeSystemDirectiveLocation : one of SetDirectiveExtension : extend directive @ Name SetArgumentsDefinitionOrExtension? Directives[Const] +# Set Operations + +With the provided syntax, we can create set operations +- `union`, or ∪⁠ (also known as **merge** or **set addition**). `union` is associative and commutative, much like addition. +- `intersect`, or ∩. +- `exclude`, or \, or - (also known as **difference** or **set subtraction**). `exclude` is neither associative nor commutative, much like subtraction. + +## Union + +```graphql +type A implements X { + field(arg: Int): String +} +``` +`union` +```graphql +type A @directive { + field(arg: Int!): String! +} +``` +will result in: +``` +type A implements X @directive { + field(arg: Int!): String +} +``` + +If a union could not possibly be commutative, i.e. the *order* of set union would affect the result, +then the `union` operation should return a `UnionError`. The type of `A.field` below cannot be determined. +``` +type A { + field: Int! +} +``` +`union` +``` +type A { + field: String! +} +``` + + +## Exclude + +## Intersect + +`A intersect B = A exclude (A exclude B)`. + +`intersect` is defined by the application of `exclude`. # Appendix: Grammar Summary diff --git a/gaps/GAP-33/metadata.yml b/gaps/GAP-33/metadata.yml index 22e3a48..c5d27be 100644 --- a/gaps/GAP-33/metadata.yml +++ b/gaps/GAP-33/metadata.yml @@ -1,12 +1,12 @@ id: 33 -title: GraphQL Schema Definition Language - Set Extensions +title: Set Extensions for Type System Documents summary: > - An auxiliary syntax extending the Schema Definition Language syntax, - unlocking set-like operations (union, exclude, intersect) on SDL documents. + An auxiliary syntax extending the Type System Document syntax, + unlocking set-like operations (union, exclude, intersect) on Schema IDL. status: proposal authors: - name: "Matt Mahoney" email: "mahoney.mattj@gmail.com" githubUsername: "@mjmahone" -sponsor: "@mjmahone" +sponsor: "@magicmark" discussion: "https://github.com/graphql/gaps/pull/33"