Skip to content

Commit cc0cef0

Browse files
First draft of new variance description
1 parent 6abcfff commit cc0cef0

File tree

1 file changed

+112
-30
lines changed

1 file changed

+112
-30
lines changed

docs/src/md/kotlin.core/declarations.md

+112-30
Original file line numberDiff line numberDiff line change
@@ -1780,56 +1780,138 @@ Only type parameters of [inline functions][Inlining] can be declared `reified`.
17801780
17811781
#### Type parameter variance
17821782
1783-
The [declaration-site variance][Mixed-site variance] of a particular type parameter for a classifier declaration is specified using special keywords `in` (for covariant parameters) and `out` (for contravariant parameters).
1783+
The [declaration-site variance][Mixed-site variance] of a particular type parameter for a classifier declaration is specified using special keywords `in` (for contravariant parameters) and `out` (for covariant parameters).
17841784
If the variance is not specified, the parameter is implicitly declared invariant.
17851785
See [the type system section][Mixed-site variance] for details.
17861786
1787-
A type parameter is **used in covariant position** in the following cases:
1787+
We would consider a type expression `E` to be **covariant** in type parameter `X` if for every concrete type expressions `T1` and `T2` such that `T1 <: T2` it holds that `E[T1 / X] <: E[T2 / X]`. One could also say, that the *position* `X` takes in `E` is **covariant**.
17881788
1789-
- It is used as an argument in another generic type and the corresponding parameter in that type is covariant;
1790-
- It is the return type of a function;
1791-
- It is a type of a property.
1789+
Not nested covariant cases are the following:
17921790
1793-
A type parameter is **used in contravariant position** in the following cases:
1791+
- A type expression is covariant in type parameter `X`, if this parameter is explicitly declared as `out` either [in the expression itself][Use-site variance] or [in the type declaration][Declaration-site variance];
1792+
- Types of functions are covariant in their return type parameter, since functions have [types of kind][Function types] `FN<in A1, ..., in AN, out R>`);
1793+
- Types of read-only properties are effectively in covariant position;
17941794
1795-
- It is used as an argument in another generic type and the corresponding parameter in that type is contravariant;
1796-
- It is a type of an parameter of a function;
1797-
- It is a type of a mutable property.
1795+
In the same way, we would consider a type expression `E` to be **contravariant** in type parameter `X` if for every concrete type expressions `T1` and `T2` such that `T1 <: T2` it holds that `E[T2 / X] <: E[T1 / X]`.
17981796
1799-
A type parameter is used in an invariant position if it is used as an argument in another generic type and the corresponding parameter in that type is invariant.
1797+
Not nested contravariant cases are the following:
1798+
1799+
- A type expression is contravariant in type parameter `X`, if this parameter is explicitly declared as `in` either [in the expression itself][Use-site variance] or [in the type declaration][Declaration-site variance];
1800+
- Types of functions are contravariant in their argument types, since functions have [types of kind][Function types] `FN<in A1, ..., in AN, out R>`;
1801+
1802+
Lastly, we would consider a type expression `E` to be **invariant** in type parameter `X` if for every concrete type expressions `T1` and `T2`, `E[T1 / X] <: E[T2 / X]` holds if and only if `T1 = T2` (`T1 <: T2` and `T2 <: T1`).
1803+
1804+
Not nested invariant cases are the following:
1805+
1806+
- A type expression is invariant in type parameter `X`, if this parameter is not declared as either `in` or `out` [in the expression itself][Use-site variance] or [in the type declaration][Declaration-site variance];
1807+
- Types of mutable properties are effectively in invariant position;
1808+
1809+
We will use `+` as a symbol denoting contravariance, `-` as a symbol denoting covariance and `o` as a symbol denoting invariance.
1810+
1811+
To infer variance of a complex type expression in a type parameter, variance composition is used, which is defined as follows:
1812+
1813+
- Given two type expressions, `A<X>` and `B<Y>`, if variance of `A<X>` in `X` is `v1`, and variance of `B<Y>` in `Y` is `v2`, then the variance of `A<B<Y>>` in `Y` is $\texttt{v1} \otimes \texttt{v2} = \texttt{v3}$, which is defined in the following table:
1814+
1815+
\begin{align*}
1816+
\texttt{+} \otimes x &= x \text{, where $x$ is any variance} \\
1817+
\texttt{-} \otimes \texttt{+} &= \texttt{-} \\
1818+
\texttt{-} \otimes \texttt{-} &= \texttt{-} \\
1819+
\texttt{o} \otimes x &= \texttt{o} \text{, where $x$ is any variance} \\
1820+
x \otimes \texttt{o} &= \texttt{o} \text{, where $x$ is any variance}
1821+
\end{align*}
1822+
1823+
This can be interpreted in the following way:
1824+
1825+
- Covariance *preserves* the given variance
1826+
- Contravariance *inverses* the given variance
1827+
- Invariance *absorbs* the given variance
1828+
1829+
Using the base not-nested cases and the rules for combining variance, we can iteratively define for every type expression its variance in a given type parameter.
1830+
1831+
When a type parameter has declared variance, and is used in a position of a different variance, a **variance conflict** may occure. The specific cases are:
1832+
1833+
- A non-invariant type parameter is used as an argument to an invariant position;
1834+
- A covariant type parameter is used as an argument to a contravariant position;
1835+
- A contravariant type parameter is used as an argument to a covariant position;
1836+
1837+
Important remark: variance conflict does not occur if the containing declaration is private to the type parameter owner (in which case its visibility is restricted, see the [visibility][Declaration visibility] section for details).
18001838
1801-
A usage of a contravariant type parameter in a covariant or invariant position, as well as usage of a covariant type parameter in a contravariant or invariant position, results in **variance conflict** and a compiler error, unless the containing declaration is private to the type parameter owner (in which case its visibility is restricted, see the [visibility][Declaration visibility] section for details).
18021839
This applies only to member declarations of the corresponding class, extensions are not subject to this limitation.
18031840
18041841
This restrictions may be lifted in particular cases by [annotating][Annotations] the corresponding type parameter usage with a special built-in annotation `kotlin.UnsafeVariance`.
18051842
By supplying this annotation the author of the code explicitly declares that safety features that variance checks provide are not needed in this particular declarations.
18061843
1807-
> Examples:
1808-
>
1844+
> Basic Examples:
1845+
>
18091846
> ```kotlin
1810-
> class Inv<T> {
1811-
> fun a(): T {...} // Ok, covariant usage
1812-
> fun b(value: T) {...} // Ok, contravariant usage
1813-
> fun c(p: Out<T>) {...} // Ok, covariant usage
1814-
> fun d(): Out<T> {...} // Ok, covariant usage
1815-
> fun e(p: In<T>) {...} // Ok, contravariant usage
1816-
> fun f(): In<T> {...} // Ok, contravariant usage
1847+
> interface Holder<T>
1848+
>
1849+
> interface Inv<T> { // T is invariant
1850+
> val a: T // Ok, covariant usage (read-only property)
1851+
> var b: T // Ok, invariant usage (mutable property)
1852+
> fun c(): T // Ok, covariant usage (return type)
1853+
> fun d(value: T) // Ok, contravariant usage (function argument)
1854+
> val e: Holder<T> // Ok, invariant usage (variance not declared)
1855+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1856+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1857+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1858+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
18171859
> }
18181860
>
1819-
> class Out<out T> { // T is covariant
1820-
> fun a(): T {...} // Ok, covariant usage
1821-
> fun b(value: T) {...} // ERROR, contravariant usage
1822-
> fun c(p: Inv<T>) {...} // ERROR, invariant usage
1823-
> fun d(): Inv<T> {...} // ERROR, invariant usage
1861+
> interface Out<out T> { // T is covariant
1862+
> val a: T // Ok, covariant usage (read-only property)
1863+
> var b: T // ERROR, invariant usage (mutable property)
1864+
> fun c(): T // Ok, covariant usage (return type)
1865+
> fun d(value: T) // ERROR, contravariant usage (function argument)
1866+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1867+
> val f: Holder<in T> // ERROR, contravariant usage (use-site contravariant)
1868+
> val f2: In<T> // ERROR, contravariant usage (delcaration-site contravariant)
1869+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1870+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
1871+
> private val h: Holder<T> // Ok, private member, no check required
18241872
> }
18251873
>
1826-
> class In<in T> { // T is contravariant
1827-
> fun a(): T {...} // ERROR, covariant usage
1828-
> fun b(value: T) {...} // Ok, contravariant usage
1829-
> fun c(p: Inv<T>) {...} // ERROR, invariant usage
1830-
> fun d(): Inv<T> {...} // ERROR, invariant usage
1874+
> interface In<in T> { // T is contravariant
1875+
> val a: T // ERROR, covariant usage (read-only property)
1876+
> var b: T // ERROR, invariant usage (mutable property)
1877+
> fun c(): T // ERROR, covariant usage (return type)
1878+
> fun d(value: T) // Ok, covariant usage (function argument)
1879+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1880+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1881+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1882+
> val g: Holder<out T> // ERROR, covariant usage (use-site covariant)
1883+
> val g2: Out<T> // ERROR, covariant usage (declaration-site contravariant)
18311884
> }
18321885
> ```
1886+
>
1887+
> More complex examples, involving variance composition
1888+
> ```kotlin
1889+
> class Foo<in T>() // T is contravariant
1890+
>
1891+
> class Bar<in S>() { // T is contravariant
1892+
> fun bar(x: Foo<S>) {} // ERROR, contravariant parameter in covariant usage
1893+
> // bar has type F1<in Foo<S>, out Unit>
1894+
> // Need to find variance of F1<in Foo<S>, out Unit> in S
1895+
> // F1<in P1, out R> is contravariant (-) in P1
1896+
> // Foo<S> is contravariant (-) in S
1897+
> // Applying variance composition: - $\otimes$ - = + (covariance)
1898+
> // Variance of F1<in Foo<S>, out Unit> in S -- covariance
1899+
> }
1900+
>
1901+
> class In<in T>() // T is contravariant
1902+
> class Out<out T>() // T is covariant
1903+
>
1904+
> class Test<in I, out O> {
1905+
> fun test(a: In<In<I>>) {} // Ok, contravariant parameter in contravariant usage
1906+
> // test has type F1<in In<In<I>>, out Unit>
1907+
> // Need to find variance of F1<in In<In<I>>, out Unit> in I
1908+
> // In<I> is contravariant (-) in I
1909+
> // In<In<I>> is covariant in I, since - $\otimes$ - = + (covariance)
1910+
> // F1<in In<In<I>>> is contravariant in I, since - $\otimes$ + = - (contravariance)
1911+
> }
1912+
> ```
1913+
>
1914+
> Remark: cases `e`, `f`, `f2`, `g`, `g2` in Basic Examples are also examples of variance composition, since they are read-only fields. However, since read-only fields are of covariance (`+`) and for any variance $x$, $\texttt{+} \otimes x = x$, it was ommited at that point.
18331915
>
18341916
> Any of these restrictions may be lifted using `@UnsafeVariance` annotation on the type argument:
18351917
>

0 commit comments

Comments
 (0)