Skip to content

Commit e90a225

Browse files
MarkTheHopefulice-phoenix
authored andcommitted
New variance explanation and model
1 parent f9a29c0 commit e90a225

File tree

2 files changed

+122
-31
lines changed

2 files changed

+122
-31
lines changed

Diff for: docs/src/md/kotlin.core/declarations.md

+121-31
Original file line numberDiff line numberDiff line change
@@ -1784,56 +1784,142 @@ Only type parameters of [inline functions][Inlining] can be declared `reified`.
17841784
17851785
#### Type parameter variance
17861786
1787-
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).
1787+
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).
17881788
If the variance is not specified, the parameter is implicitly declared invariant.
17891789
See [the type system section][Mixed-site variance] for details.
17901790
1791-
A type parameter is **used in covariant position** in the following cases:
1791+
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**.
17921792
1793-
- It is used as an argument in another generic type and the corresponding parameter in that type is covariant;
1794-
- It is the return type of a function;
1795-
- It is a type of a property.
1793+
Base covariant cases are the following:
17961794
1797-
A type parameter is **used in contravariant position** in the following cases:
1795+
- 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];
1796+
- 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>`);
1797+
- Types of read-only properties are effectively in covariant position;
17981798
1799-
- It is used as an argument in another generic type and the corresponding parameter in that type is contravariant;
1800-
- It is a type of an parameter of a function;
1801-
- It is a type of a mutable property.
1799+
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]`.
18021800
1803-
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.
1801+
Base contravariant cases are the following:
1802+
1803+
- 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];
1804+
- Types of functions are contravariant in their argument types, since functions have [types of kind][Function types] `FN<in A1, ..., in AN, out R>`;
1805+
1806+
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`).
1807+
1808+
Base invariant cases are the following:
1809+
1810+
- 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];
1811+
- Types of mutable properties are effectively in invariant position;
1812+
1813+
We will use `+` as a symbol denoting covariance, `-` as a symbol denoting contravariance and `o` as a symbol denoting invariance.
1814+
1815+
To infer variance of a complex type expression in a type parameter, variance composition is used, which is defined as follows:
1816+
1817+
- 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:
1818+
1819+
\begin{align*}
1820+
\texttt{+} \otimes x &= x \text{, where $x$ is any variance} \\
1821+
\texttt{-} \otimes \texttt{+} &= \texttt{-} \\
1822+
\texttt{-} \otimes \texttt{-} &= \texttt{+} \\
1823+
\texttt{o} \otimes x &= \texttt{o} \text{, where $x$ is any variance} \\
1824+
x \otimes \texttt{o} &= \texttt{o} \text{, where $x$ is any variance}
1825+
\end{align*}
1826+
1827+
This can be interpreted in the following way:
1828+
1829+
- Covariance *preserves* the given variance
1830+
- Contravariance *inverses* the given variance
1831+
- Invariance *absorbs* the given variance
1832+
1833+
Further reasoning and formalization behind this system is available in the ["Taming the Wildcards" paper][declarations.references].
1834+
1835+
[declarations.references]: #declarations.references
1836+
1837+
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.
1838+
1839+
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:
1840+
1841+
- A non-invariant type parameter is used as an argument to an invariant position;
1842+
- A covariant type parameter is used as an argument to a contravariant position;
1843+
- A contravariant type parameter is used as an argument to a covariant position;
1844+
1845+
> 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).
18041846
1805-
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).
18061847
This applies only to member declarations of the corresponding class, extensions are not subject to this limitation.
18071848
1808-
This restrictions may be lifted in particular cases by [annotating][Annotations] the corresponding type parameter usage with a special built-in annotation `kotlin.UnsafeVariance`.
1849+
These restrictions may be lifted in particular cases by [annotating][Annotations] the corresponding type parameter usage with a special built-in annotation `kotlin.UnsafeVariance`.
18091850
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.
18101851
1811-
> Examples:
1812-
>
1852+
> Basic examples:
1853+
>
18131854
> ```kotlin
1814-
> class Inv<T> {
1815-
> fun a(): T {...} // Ok, covariant usage
1816-
> fun b(value: T) {...} // Ok, contravariant usage
1817-
> fun c(p: Out<T>) {...} // Ok, covariant usage
1818-
> fun d(): Out<T> {...} // Ok, covariant usage
1819-
> fun e(p: In<T>) {...} // Ok, contravariant usage
1820-
> fun f(): In<T> {...} // Ok, contravariant usage
1855+
> interface Holder<T>
1856+
>
1857+
> interface Inv<T> { // T is invariant
1858+
> val a: T // Ok, covariant usage (read-only property)
1859+
> var b: T // Ok, invariant usage (mutable property)
1860+
> fun c(): T // Ok, covariant usage (return type)
1861+
> fun d(value: T) // Ok, contravariant usage (function argument)
1862+
> val e: Holder<T> // Ok, invariant usage (variance not declared)
1863+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1864+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1865+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1866+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
18211867
> }
18221868
>
1823-
> class Out<out T> { // T is covariant
1824-
> fun a(): T {...} // Ok, covariant usage
1825-
> fun b(value: T) {...} // ERROR, contravariant usage
1826-
> fun c(p: Inv<T>) {...} // ERROR, invariant usage
1827-
> fun d(): Inv<T> {...} // ERROR, invariant usage
1869+
> interface Out<out T> { // T is covariant
1870+
> val a: T // Ok, covariant usage (read-only property)
1871+
> var b: T // ERROR, invariant usage (mutable property)
1872+
> fun c(): T // Ok, covariant usage (return type)
1873+
> fun d(value: T) // ERROR, contravariant usage (function argument)
1874+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1875+
> val f: Holder<in T> // ERROR, contravariant usage (use-site contravariant)
1876+
> val f2: In<T> // ERROR, contravariant usage (delcaration-site contravariant)
1877+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1878+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
1879+
> private val h: Holder<T> // Ok, private member, no check required
18281880
> }
18291881
>
1830-
> class In<in T> { // T is contravariant
1831-
> fun a(): T {...} // ERROR, covariant usage
1832-
> fun b(value: T) {...} // Ok, contravariant usage
1833-
> fun c(p: Inv<T>) {...} // ERROR, invariant usage
1834-
> fun d(): Inv<T> {...} // ERROR, invariant usage
1882+
> interface In<in T> { // T is contravariant
1883+
> val a: T // ERROR, covariant usage (read-only property)
1884+
> var b: T // ERROR, invariant usage (mutable property)
1885+
> fun c(): T // ERROR, covariant usage (return type)
1886+
> fun d(value: T) // Ok, covariant usage (function argument)
1887+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1888+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1889+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1890+
> val g: Holder<out T> // ERROR, covariant usage (use-site covariant)
1891+
> val g2: Out<T> // ERROR, covariant usage (declaration-site contravariant)
18351892
> }
18361893
> ```
1894+
>
1895+
> More complex examples, involving variance composition
1896+
> ```kotlin
1897+
> class Foo<in T>() // T is contravariant
1898+
>
1899+
> class Bar<in S>() { // S is contravariant
1900+
> fun bar(x: Foo<S>) {} // ERROR, contravariant parameter in covariant usage
1901+
> // bar has type F1<in Foo<S>, out Unit>
1902+
> // Need to find variance of F1<in Foo<S>, out Unit> in S
1903+
> // F1<in P1, out R> is contravariant (-) in P1
1904+
> // Foo<S> is contravariant (-) in S
1905+
> // Applying variance composition: - ⊗ - = + (covariance)
1906+
> // Variance of F1<in Foo<S>, out Unit> in S -- covariance
1907+
> }
1908+
>
1909+
> class In<in T>() // T is contravariant
1910+
> class Out<out T>() // T is covariant
1911+
>
1912+
> class Test<in I, out O> {
1913+
> fun test(a: In<In<I>>) {} // Ok, contravariant parameter in contravariant usage
1914+
> // test has type F1<in In<In<I>>, out Unit>
1915+
> // Need to find variance of F1<in In<In<I>>, out Unit> in I
1916+
> // In<I> is contravariant (-) in I
1917+
> // In<In<I>> is covariant in I, since - ⊗ - = + (covariance)
1918+
> // F1<in In<In<I>>> is contravariant in I, since - ⊗ + = - (contravariance)
1919+
> }
1920+
> ```
1921+
>
1922+
> 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 omitted at that point.
18371923
>
18381924
> Any of these restrictions may be lifted using `@UnsafeVariance` annotation on the type argument:
18391925
>
@@ -1989,3 +2075,7 @@ There is a partial order of *weakness* between different visibility modifiers:
19892075
> ```
19902076
>
19912077
2078+
### References {#declarations.references}
2079+
2080+
1. John Altidor, Shan Shan Huang, and Yannis Smaragdakis. "Taming the wildcards: combining definition- and use-site variance." 2011 (\url{https://yanniss.github.io/variance-pldi11.pdf})
2081+

Diff for: docs/src/md/preamble.tex

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
\newunicodechar{⊆}{$\subseteq$}
2828
\newunicodechar{⊤}{$\top$}
2929
\newunicodechar{⊥}{$\bot$}
30+
\newunicodechar{⊗}{$\otimes$}
3031

3132
\hyphenation{
3233
Sus-pend-Co-ro-u-ti-ne-Un-in-ter-cep-ted-Or-Re-turn

0 commit comments

Comments
 (0)