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

Add section about variance and positions #6105

Merged
merged 12 commits into from
Oct 16, 2024
81 changes: 73 additions & 8 deletions src/content/resources/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ For additional details, see the documentation on [Functions][_functions_].

## Irrefutable pattern

_Irrefutable patterns_ are patterns that always match.
_Irrefutable patterns_ are patterns that always match.
Irrefutable patterns are the only patterns that can appear in
_irrefutable contexts_: the [_declaration_][] and [_assignment_][]
_irrefutable contexts_: the [_declaration_][] and [_assignment_][]
pattern contexts.

[_declaration_]: /language/patterns#variable-declaration
[_declaration_]: /language/patterns#variable-declaration
[_assignment_]: /language/patterns#variable-assignment

## Mixin application
Expand Down Expand Up @@ -274,7 +274,7 @@ directory but not inside the `lib/src` directory.
## Refutable pattern

A _refutable pattern_ is a pattern that can be tested against a value to
determine if the pattern matches the value.
determine if the pattern matches the value.
If not, the pattern _refutes_, or denies, the match.
Refutable patterns appear in [_matching contexts_][].

Expand All @@ -286,7 +286,7 @@ A _subclass_ is a class that inherits the implementation of another class by usi
[`extends`](/language/extend) keyword, or by [mixin application](#mixin-application).

```dart
class A extends B {} // A is a subclass of B; B is the superclass of A.
class A extends B {} // A is a subclass of B; B is the superclass of A.

class B1 extends A with M {} // B1 has the superclass `A with M`, which has the superclass A.
```
Expand All @@ -295,7 +295,7 @@ A subclass relation also implies an associated [subtype](#subtype) relation.
For example, `class A` implicitly defines an associated type `A`
which instances of the class `A` inhabit.
So, `class A extends B` declares not just that the class
`A` is a subclass of `B`, but also establishes that the *type* `A` is a
`A` is a subclass of `B`, but also establishes that the *type* `A` is a
*subtype* of the type `B`.

Subclass relations are a subset of subtype relations.
Expand All @@ -310,7 +310,7 @@ A _subtype_ relation is where a value of a certain type is substitutable
where the value of another type, the supertype, is expected.
For example, if `S` is a subtype of `T`,
then you can substitute a value of type `S`
where a value of type `T` is expected.
where a value of type `T` is expected.

A subtype supports all of the operations of its supertype
(and possibly some extra operations).
Expand All @@ -320,7 +320,7 @@ and all of the methods of the supertype are available on the subtype.

This is true at least statically.
A specific API might not allow the substitution at run time,
depending on its operations.
depending on its operations.

Some subtype relations are based on the structure of the type,
like with nullable types (for example, `int` is a subtype of `int?`)
Expand All @@ -336,3 +336,68 @@ class A implements B {} // A is a subtype of B, but NOT a subclass of B.

class C extends D {} // C is a subtype AND a subclass of D.
```

## Variance and variance positions
eernstg marked this conversation as resolved.
Show resolved Hide resolved

A type parameter of a class (or other type declaration, like a mixin) is
said to be _covariant_ when the type as a whole "co-varies" with the actual
type argument. In other words, if the type argument is replaced by a
subtype then the type as a whole is also a subtype.

For example, the type parameter of the class `List` is covariant because
list types co-vary with their type argument: `List<int>` is a subtype of
`List<Object>` because `int` is a subtype of `Object`.

In Dart, all type parameters of all class, mixin, mixin class, and enum
declarations are covariant.

However, function types are different: A function type is covariant in its
return type, but the opposite (known as _contravariant_) in its parameter
types. For example, the type `int Function(int)` is a subtype of the type
`Object Function(int)`, but it is a supertype of `int Function(Object)`.

This makes sense if you consider their [substitutability](#subtype). If you
call a function with a static type of `int Function(int)`, that function
can actually be of type `int Function(Object)` at runtime. Based on the
static type, you expect to be able to pass an `int` to it. That will be
fine since the function actually accepts any `Object`, and this includes
every object of type `int`. Similarly, the returned result will be of type
`int`, which is also what you expect based on the static type.

Hence, `int Function(Object)` is a subtype of `int Function(int)`.

Note that everything is turned upside-down for parameter types. In
particular, this subtype relation among function types requires that the
_opposite_ subtype relation exists for the parameter type (for example,
`void Function(Object)` is a subtype of `void Function(int)` because `int`
is a subtype of `Object`).
eernstg marked this conversation as resolved.
Show resolved Hide resolved

With a more complex type like `List<void Function(int)>`, you have to
consider the _positions_ in the type. This is basically just a matter of
turning one of the parts of the type into a placeholder (let's use `_` for
that), and then consider what happens to the type when different types are
eernstg marked this conversation as resolved.
Show resolved Hide resolved
placed in that position.

For example, consider `List<void Function(_)>` as a template for a type
where you can put different types in place of the placeholder `_`. This
type is contravariant in the position where that placeholder occurs.

We can illustrate this as follows. `List<void Function(Object)>` is a
eernstg marked this conversation as resolved.
Show resolved Hide resolved
subtype of `List<void Function(int)>` because `void Function(Object)` is a
subtype of `void Function(int)` because `void` is a subtype of `void` (the
return types) and `int` is a subtype of `Object` (the parameter types, in
the opposite order). Hence, the type at `_` varies in the opposite
direction of the type `List<void Function(_)>` as a whole, and this
'opposite direction' by definition makes it a _contravariant position_.

A _covariant position_ is defined similarly. For example, `_` is at a
covariant position in the type `List<_>`, and `_` is also at a covarinat
eernstg marked this conversation as resolved.
Show resolved Hide resolved
position in the type `_ Function(int)`.

There is yet another kind of position known as _invariant_, but it occurs
much more rarely so the details are omitted here.

In practice, it's often sufficient to know that the type arguments of a
class, mixin, etc. are in a covariant position, and so is the return type
of a function type, but the parameter types are in a contravariant
position.