Skip to content

Commit 7724685

Browse files
New variance explanation and model
1 parent 6abcfff commit 7724685

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
@@ -1780,56 +1780,142 @@ 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+
Base 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+
Base 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+
Base 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 covariance, `-` as a symbol denoting contravariance 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+
Further reasoning and formalization behind this system is available in the ["Taming the Wildcards" paper][declarations.references].
1830+
1831+
[declarations.references]: #declarations.references
1832+
1833+
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.
1834+
1835+
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:
1836+
1837+
- A non-invariant type parameter is used as an argument to an invariant position;
1838+
- A covariant type parameter is used as an argument to a contravariant position;
1839+
- A contravariant type parameter is used as an argument to a covariant position;
1840+
1841+
> 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).
18001842
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).
18021843
This applies only to member declarations of the corresponding class, extensions are not subject to this limitation.
18031844
1804-
This restrictions may be lifted in particular cases by [annotating][Annotations] the corresponding type parameter usage with a special built-in annotation `kotlin.UnsafeVariance`.
1845+
These restrictions may be lifted in particular cases by [annotating][Annotations] the corresponding type parameter usage with a special built-in annotation `kotlin.UnsafeVariance`.
18051846
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.
18061847
1807-
> Examples:
1808-
>
1848+
> Basic examples:
1849+
>
18091850
> ```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
1851+
> interface Holder<T>
1852+
>
1853+
> interface Inv<T> { // T is invariant
1854+
> val a: T // Ok, covariant usage (read-only property)
1855+
> var b: T // Ok, invariant usage (mutable property)
1856+
> fun c(): T // Ok, covariant usage (return type)
1857+
> fun d(value: T) // Ok, contravariant usage (function argument)
1858+
> val e: Holder<T> // Ok, invariant usage (variance not declared)
1859+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1860+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1861+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1862+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
18171863
> }
18181864
>
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
1865+
> interface Out<out T> { // T is covariant
1866+
> val a: T // Ok, covariant usage (read-only property)
1867+
> var b: T // ERROR, invariant usage (mutable property)
1868+
> fun c(): T // Ok, covariant usage (return type)
1869+
> fun d(value: T) // ERROR, contravariant usage (function argument)
1870+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1871+
> val f: Holder<in T> // ERROR, contravariant usage (use-site contravariant)
1872+
> val f2: In<T> // ERROR, contravariant usage (delcaration-site contravariant)
1873+
> val g: Holder<out T> // Ok, covariant usage (use-site covariant)
1874+
> val g2: Out<T> // Ok, covariant usage (declaration-site covariant)
1875+
> private val h: Holder<T> // Ok, private member, no check required
18241876
> }
18251877
>
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
1878+
> interface In<in T> { // T is contravariant
1879+
> val a: T // ERROR, covariant usage (read-only property)
1880+
> var b: T // ERROR, invariant usage (mutable property)
1881+
> fun c(): T // ERROR, covariant usage (return type)
1882+
> fun d(value: T) // Ok, covariant usage (function argument)
1883+
> val e: Holder<T> // ERROR, invariant usage (variance not declared)
1884+
> val f: Holder<in T> // Ok, contravariant usage (use-site contravariant)
1885+
> val f2: In<T> // Ok, contravariant usage (declaration-site contravariant)
1886+
> val g: Holder<out T> // ERROR, covariant usage (use-site covariant)
1887+
> val g2: Out<T> // ERROR, covariant usage (declaration-site contravariant)
18311888
> }
18321889
> ```
1890+
>
1891+
> More complex examples, involving variance composition
1892+
> ```kotlin
1893+
> class Foo<in T>() // T is contravariant
1894+
>
1895+
> class Bar<in S>() { // S is contravariant
1896+
> fun bar(x: Foo<S>) {} // ERROR, contravariant parameter in covariant usage
1897+
> // bar has type F1<in Foo<S>, out Unit>
1898+
> // Need to find variance of F1<in Foo<S>, out Unit> in S
1899+
> // F1<in P1, out R> is contravariant (-) in P1
1900+
> // Foo<S> is contravariant (-) in S
1901+
> // Applying variance composition: - ⊗ - = + (covariance)
1902+
> // Variance of F1<in Foo<S>, out Unit> in S -- covariance
1903+
> }
1904+
>
1905+
> class In<in T>() // T is contravariant
1906+
> class Out<out T>() // T is covariant
1907+
>
1908+
> class Test<in I, out O> {
1909+
> fun test(a: In<In<I>>) {} // Ok, contravariant parameter in contravariant usage
1910+
> // test has type F1<in In<In<I>>, out Unit>
1911+
> // Need to find variance of F1<in In<In<I>>, out Unit> in I
1912+
> // In<I> is contravariant (-) in I
1913+
> // In<In<I>> is covariant in I, since - ⊗ - = + (covariance)
1914+
> // F1<in In<In<I>>> is contravariant in I, since - ⊗ + = - (contravariance)
1915+
> }
1916+
> ```
1917+
>
1918+
> 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.
18331919
>
18341920
> Any of these restrictions may be lifted using `@UnsafeVariance` annotation on the type argument:
18351921
>
@@ -1985,3 +2071,7 @@ There is a partial order of *weakness* between different visibility modifiers:
19852071
> ```
19862072
>
19872073
2074+
### References {#declarations.references}
2075+
2076+
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})
2077+

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)