Skip to content

Commit d7c0a89

Browse files
authored
Update nominative-union-types.md
1 parent cde6602 commit d7c0a89

File tree

1 file changed

+17
-1
lines changed

1 file changed

+17
-1
lines changed

working/union-types/nominative-union-types.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ It’s a subtype of `Object` if all the elements types are subtypes of `Object`.
3535

3636
If `F` is nullable, then `F?` is equivalent to `F` (mutual subtypes), otherwise `F?` is a proper supertype of `F`.
3737

38-
The type is a supertype of each of its union element types (`Foo` is a supertype of `A` and `B` here.).
38+
The type is a supertype of each of its union element types (`F` is a supertype of `T1` .. `Tn` here.).
3939

4040
If the union type is generic, different instantiations can be subtypes of each other. The type parameters vary by their occurrences, like for a type alias. For example `typedef Foo<T> = T Function(int) | int Function(T);` is invariant in `T` because `T` occurs both covariantly and contravariantly in the union element types. _(This might need us to introduce variance first.)_. For `typedef G<X> = List<X> | Set<X>;`, `G<int>` is a subtype of `G<num>`, because `X` occurs only covariantly, so `G` varies covariantly with `X`. _A direct use of the type variable, like `typedef U<S, T> = S | T;` counts as covariant._
4141

@@ -127,6 +127,22 @@ In every other way, the union type is just a normal type, with the subtype relat
127127

128128
## Limitations and discussions
129129

130+
### The subtying rules don't actually *work*
131+
132+
if you have `abstract class C implements List<C> {}` and want to check whether `C` is a subtype of `Json`, as defined above,
133+
then you eventually have to try checking whether `C` is a subtype of `List<Json>`. Since `C` is a `List<C>`, all you need to
134+
check is whether `C` is a subtype of `Json`. Whoops.
135+
136+
Which means that we probably need to prevent *any* cyclic references in the union types, which again makes them much less
137+
useful for actual recursive types like `Json`.
138+
139+
We can declare classes for such a structure, for example sealed classes like `sealed class Json {}`, `class JsonValue<T> extends Json { T get value; }`, `class JsonList extends Json implements List<Json>` and `class JsonMap extends Json implements Map<String, Json>`.
140+
That's not the same. Checking whether something is a `Json` is simple, the object itself knows that. Whether it's a `JsonList` too.
141+
For the union `Json` type, there really is an infinite number of different subtypes that we need to potentially check for in order to determine whether a value belongs to the set of accepted values.
142+
The new type introduced is not a type that any object *inherently* implements.
143+
144+
(Should we drop the subtyping, and only have assignability into the union type? No type relations between union types and non-union types other than `Never`, `Object` or `Object?`? Then a `List<C>` is-not-a `List<Json>` and vice-versa. And a `List<int>` is not a `List<Json>` either, only `List<Json>` itself is. Much more restricted, definitely.)
145+
130146
### Incompatible with existing `dynamic`-using types
131147

132148
This does not allow having a simple type alias for existing JSON values,

0 commit comments

Comments
 (0)