Skip to content

Commit

Permalink
Merge branch 'main' into feat/update-new-site-shared-structure
Browse files Browse the repository at this point in the history
  • Loading branch information
parlough authored Oct 16, 2024
2 parents 2ab976d + 6735643 commit ab5db71
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 8 deletions.
11 changes: 11 additions & 0 deletions examples/misc/lib/language_tour/functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ void miscDeclAnalyzedButNotTested() {
// #enddocregion function-shorthand
}

{
// #docregion function-type
void greet(String name, {String greeting = 'Hello'}) =>
print('$greeting $name!');

// Store `greet` in a variable and call it.
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
// #enddocregion function-type
}

{
// #docregion specify-named-parameters
/// Sets the [bold] and [hidden] flags ...
Expand Down
2 changes: 2 additions & 0 deletions src/content/language/built-in-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The Dart language has special support for the following:
- [Strings](#strings) (`String`)
- [Booleans](#booleans) (`bool`)
- [Records][] (`(value1, value2)`)
- [Functions][] (`Function`)
- [Lists][] (`List`, also known as *arrays*)
- [Sets][] (`Set`)
- [Maps][] (`Map`)
Expand Down Expand Up @@ -393,6 +394,7 @@ Symbol literals are compile-time constants.


[Records]: /language/records
[Functions]: /language/functions#function-types
[Lists]: /language/collections#lists
[Sets]: /language/collections#sets
[Maps]: /language/collections#maps
Expand Down
29 changes: 29 additions & 0 deletions src/content/language/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,35 @@ assert(loudify('hello') == '!!! HELLO !!!');
This example uses an anonymous function.
More about those in the next section.

## Function types

You can specify the type of a function, which is known as a *function type*.
A function type is obtained from a function declaration header by
replacing the function name by the keyword `Function`.
Moreover, you are allowed to omit the names of positional parameters, but
the names of named parameters can't be omitted. For example:

<?code-excerpt "misc/lib/language_tour/functions.dart (function-type)"?>
```dart
void greet(String name, {String greeting = 'Hello'}) =>
print('$greeting $name!');
// Store `greet` in a variable and call it.
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
```

:::note
In Dart, functions are first-class objects,
meaning they can be assigned to variables,
passed as arguments, and returned from other functions.

You can use a [`typedef`][] declaration to explicitly name function types,
which can be useful for clarity and reusability.
:::

[`typedef`]: /language/typedefs

## Anonymous functions

Though you name most functions, such as `main()` or `printElement()`.
Expand Down
85 changes: 77 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,72 @@ 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 {:#variance}

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`.

With a more complex type like `List<void Function(int)>`,
you have to consider the _positions_ in the type.
To accomplish this, turn one of the parts of the type into a placeholder,
and then consider what happens to the type when
different types are 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.

The following illustrates this by substituting `Object` and `int` for `_`.
`List<void Function(Object)>` is a 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 covariant 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.

0 comments on commit ab5db71

Please sign in to comment.