Skip to content

Commit 1a0a5a2

Browse files
Merge pull request #8706 from iamrecursion/wip/ara/match-types-doc-tweaks
Fix some mistakes in the Match Types documentation
2 parents 979d097 + ba3aa8c commit 1a0a5a2

File tree

1 file changed

+104
-56
lines changed

1 file changed

+104
-56
lines changed

docs/docs/reference/new-types/match-types.md

Lines changed: 104 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ layout: doc-page
33
title: "Match Types"
44
---
55

6-
A match type reduces to one of its right-hand sides, depending on a scrutinee type. Example:
6+
A match type reduces to one of its right-hand sides, depending on the type of
7+
its scrutinee. For example:
78

89
```scala
910
type Elem[X] = X match {
@@ -22,16 +23,17 @@ Elem[List[Float]] =:= Float
2223
Elem[Nil.type] =:= Nothing
2324
```
2425

25-
Here `=:=` is understood to mean that left and right hand sides are mutually subtypes of each other.
26+
Here `=:=` is understood to mean that left and right hand sides are mutually
27+
subtypes of each other.
2628

2729
In general, a match type is of the form
2830

2931
```scala
3032
S match { P1 => T1 ... Pn => Tn }
3133
```
3234

33-
where `S`, `T1`, ..., `Tn` are types and `P1`, ..., `Pn` are type patterns. Type variables
34-
in patterns start as usual with a lower case letter.
35+
where `S`, `T1`, ..., `Tn` are types and `P1`, ..., `Pn` are type patterns. Type
36+
variables in patterns start with a lower case letter, as usual.
3537

3638
Match types can form part of recursive type definitions. Example:
3739

@@ -53,11 +55,16 @@ type Concat[Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match {
5355
}
5456
```
5557

56-
In this definition, every instance of `Concat[A, B]`, whether reducible or not, is known to be a subtype of `Tuple`. This is necessary to make the recursive invocation `x *: Concat[xs, Ys]` type check, since `*:` demands a `Tuple` as its right operand.
58+
In this definition, every instance of `Concat[A, B]`, whether reducible or not,
59+
is known to be a subtype of `Tuple`. This is necessary to make the recursive
60+
invocation `x *: Concat[xs, Ys]` type check, since `*:` demands a `Tuple` as its
61+
right operand.
5762

58-
## Dependent typing
63+
## Dependent Typing
5964

60-
Match types can be used to define dependently type methods. For instance, here is value level counterpart to the`LeafElem` defined above (note the use of the match type as return type):
65+
Match types can be used to define dependently typed methods. For instance, here
66+
is the value level counterpart to the `LeafElem` type defined above (note the
67+
use of the match type as the return type):
6168

6269
```scala
6370
def leafElem[X](x: X): LeafElem[X] = x match {
@@ -68,12 +75,16 @@ def leafElem[X](x: X): LeafElem[X] = x match {
6875
}
6976
```
7077

71-
This special mode of typing for match expressions is only used when the following conditions are met:
78+
This special mode of typing for match expressions is only used when the
79+
following conditions are met:
7280

7381
1. The match expression patterns do not have guards
74-
2. The match expression scrutinee's type is a subtype of the match type scrutinee's type
82+
2. The match expression scrutinee's type is a subtype of the match type
83+
scrutinee's type
7584
3. The match expression and the match type have the same number of cases
76-
4. The match expression patterns are all [Typed Patterns](https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#typed-patterns), and these types are `=:=` to their corresponding type patterns in the match type
85+
4. The match expression patterns are all [Typed Patterns](https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#typed-patterns),
86+
and these types are `=:=` to their corresponding type patterns in the match
87+
type
7788

7889
## Representation of Match Types
7990

@@ -86,77 +97,102 @@ is `Match(S, C1, ..., Cn) <: B` where each case `Ci` is of the form
8697
[Xs] =>> P => T
8798
```
8899

89-
Here, `[Xs]` is a type parameter clause of the variables bound in pattern `Pi`. If there are no bound type variables in a case, the type parameter clause is omitted and only the function type `P => T` is kept. So each case is either a unary function type or a type lambda over a unary function type.
100+
Here, `[Xs]` is a type parameter clause of the variables bound in pattern `Pi`.
101+
If there are no bound type variables in a case, the type parameter clause is
102+
omitted and only the function type `P => T` is kept. So each case is either a
103+
unary function type or a type lambda over a unary function type.
90104

91-
`B` is the declared upper bound of the match type, or `Any` if no such bound is given.
92-
We will leave it out in places where it does not matter for the discussion. Scrutinee, bound and pattern types must be first-order types.
105+
`B` is the declared upper bound of the match type, or `Any` if no such bound is
106+
given. We will leave it out in places where it does not matter for the
107+
discussion. The scrutinee, bound, and pattern types must all be first-order
108+
types.
93109

94-
## Match type reduction
110+
## Match Type Reduction
95111

96-
Match type reduction follows the semantics of match expression, that is, a match type of the form `S match { P1 => T1 ... Pn => Tn }` reduces to `Ti` if and only if `s: S match { _: P1 => T1 ... _: Pn => Tn }` evaluates to a value of type `Ti` for all `s: S`.
112+
Match type reduction follows the semantics of match expressions, that is, a
113+
match type of the form `S match { P1 => T1 ... Pn => Tn }` reduces to `Ti` if
114+
and only if `s: S match { _: P1 => T1 ... _: Pn => Tn }` evaluates to a value of
115+
type `Ti` for all `s: S`.
97116

98117
The compiler implements the following reduction algorithm:
99118

100-
- If the scrutinee type `S` is an empty set of values (such as `Nothing` or `String & Int`), do not reduce.
119+
- If the scrutinee type `S` is an empty set of values (such as `Nothing` or
120+
`String & Int`), do not reduce.
101121
- Sequentially consider each pattern `Pi`
102122
- If `S <: Pi` reduce to `Ti`.
103-
- Otherwise, try constructing a proof that `S` and `Pi` are disjoint, or, in other words, that no value `s` of type `S` is also of type `Pi`.
104-
- If such proof is found, proceed to the next case (`Pi+1`), otherwise, do not reduce.
123+
- Otherwise, try constructing a proof that `S` and `Pi` are disjoint, or, in
124+
other words, that no value `s` of type `S` is also of type `Pi`.
125+
- If such proof is found, proceed to the next case (`Pi+1`), otherwise, do
126+
not reduce.
105127

106128
Disjointness proofs rely on the following properties of Scala types:
107129

108130
1. Single inheritance of classes
109131
2. Final classes cannot be extended
110132
3. Constant types with distinct values are nonintersecting
111133

112-
Type parameters in patterns are minimally instantiated when computing `S <: Pi`. An instantiation `Is` is _minimal_ for `Xs` if all type variables in `Xs` that appear covariantly and nonvariantly in `Is` are as small as possible and all type variables in `Xs` that appear contravariantly in `Is` are as large as possible. Here, "small" and "large" are understood with respect to `<:`.
134+
Type parameters in patterns are minimally instantiated when computing `S <: Pi`.
135+
An instantiation `Is` is _minimal_ for `Xs` if all type variables in `Xs` that
136+
appear covariantly and nonvariantly in `Is` are as small as possible and all
137+
type variables in `Xs` that appear contravariantly in `Is` are as large as
138+
possible. Here, "small" and "large" are understood with respect to `<:`.
113139

114-
For simplicity, we have omitted constraint handling so far. The full formulation of subtyping tests describes them as a function from a constraint and a pair of types to either _success_ and a new constraint or _failure_. In the context of reduction, the subtyping test `S <: [Xs := Is] P` is understood to leave the bounds of all variables in the input constraint unchanged, i.e. existing variables in the constraint cannot be instantiated by matching the scrutinee against the patterns.
140+
For simplicity, we have omitted constraint handling so far. The full formulation
141+
of subtyping tests describes them as a function from a constraint and a pair of
142+
types to either _success_ and a new constraint or _failure_. In the context of
143+
reduction, the subtyping test `S <: [Xs := Is] P` is understood to leave the
144+
bounds of all variables in the input constraint unchanged, i.e. existing
145+
variables in the constraint cannot be instantiated by matching the scrutinee
146+
against the patterns.
115147

116148
## Subtyping Rules for Match Types
117149

118-
The following rules apply to match types. For simplicity, we omit environments and constraints.
150+
The following rules apply to match types. For simplicity, we omit environments
151+
and constraints.
119152

120-
The first rule is a structural comparison between two match types:
153+
1. The first rule is a structural comparison between two match types:
121154

122-
```
123-
S match { P1 => T1 ... Pm => Tm } <: T match { Q1 => U1 ... Qn => Un }
124-
```
155+
```
156+
S match { P1 => T1 ... Pm => Tm } <: T match { Q1 => U1 ... Qn => Un }
157+
```
125158

126-
if
159+
if
127160

128-
```
129-
S =:= T, m >= n, Pi =:= Qi and Ti <: Ui for i in 1..n
130-
```
161+
```
162+
S =:= T, m >= n, Pi =:= Qi and Ti <: Ui for i in 1..n
163+
```
131164

132-
I.e. scrutinees and patterns must be equal and the corresponding bodies must be subtypes. No case re-ordering is allowed, but the subtype can have more cases than the supertype.
165+
I.e. scrutinees and patterns must be equal and the corresponding bodies must
166+
be subtypes. No case re-ordering is allowed, but the subtype can have more
167+
cases than the supertype.
133168

134-
The second rule states that a match type and its redux are mutual subtypes
169+
2. The second rule states that a match type and its redux are mutual subtypes.
135170

136-
```
137-
S match { P1 => T1 ... Pn => Tn } <: U
138-
U <: S match { P1 => T1 ... Pn => Tn }
139-
```
171+
```
172+
S match { P1 => T1 ... Pn => Tn } <: U
173+
U <: S match { P1 => T1 ... Pn => Tn }
174+
```
140175

141-
if
176+
if
142177

143-
```
144-
S match { P1 => T1 ... Pn => Tn } reduces-to U
145-
```
178+
```
179+
S match { P1 => T1 ... Pn => Tn } reduces-to U
180+
```
146181

147-
The third rule states that a match type conforms to its upper bound:
182+
3. The third rule states that a match type conforms to its upper bound:
148183

149-
```
150-
(S match { P1 => T1 ... Pn => Tn } <: B) <: B
151-
```
184+
```
185+
(S match { P1 => T1 ... Pn => Tn } <: B) <: B
186+
```
152187

153188
## Termination
154189

155190
Match type definitions can be recursive, which means that it's possible to run
156191
into an infinite loop while reducing match types.
157192

158-
Since reduction is linked to subtyping, we already have a cycle detection mechanism in place.
159-
So the following will already give a reasonable error message:
193+
Since reduction is linked to subtyping, we already have a cycle detection
194+
mechanism in place. As a result, the following will already give a reasonable
195+
error message:
160196

161197
```scala
162198
type L[X] = X match {
@@ -176,26 +212,38 @@ def g[X]: L[X] = ???
176212
| subtype LazyRef(Test.L[Int]) <:< Int
177213
```
178214

179-
Internally, `dotc` detects these cycles by turning selected stackoverflows
180-
into type errors. If there is a stackoverflow during subtyping, the exception
181-
will be caught and turned into a compile-time error that indicates a trace of
182-
the subtype tests that caused the overflow without showing a full stacktrace.
215+
Internally, `dotc` detects these cycles by turning selected stack overflows into
216+
type errors. If there is a stack overflow during subtyping, the exception will
217+
be caught and turned into a compile-time error that indicates a trace of the
218+
subtype tests that caused the overflow without showing a full stack trace.
183219

184220
## Variance Laws for Match Types
221+
NOTE: This section does not reflect the current implementation.
185222

186-
Within a match type `Match(S, Cs) <: B`, all occurrences of type variables count as covariant. By the nature of the cases `Ci` this means that occurrences in pattern position are contravarant (since patterns are represented as function type arguments).
223+
Within a match type `Match(S, Cs) <: B`, all occurrences of type variables count
224+
as covariant. By the nature of the cases `Ci` this means that occurrences in
225+
pattern position are contravarant (since patterns are represented as function
226+
type arguments).
187227

188228
## Related Work
189229

190-
Match types have similarities with [closed type families](https://wiki.haskell.org/GHC/Type_families) in Haskell. Some differences are:
230+
Match types have similarities with
231+
[closed type families](https://wiki.haskell.org/GHC/Type_families) in Haskell.
232+
Some differences are:
191233

192-
- Subtyping instead of type equalities.
193-
- Match type reduction does not tighten the underlying constraint, whereas type family reduction does unify. This difference in approach mirrors the difference between local type inference in Scala and global type inference in Haskell.
234+
- Subtyping instead of type equalities.
235+
- Match type reduction does not tighten the underlying constraint, whereas type
236+
family reduction does unify. This difference in approach mirrors the
237+
difference between local type inference in Scala and global type inference in
238+
Haskell.
194239

195-
Match types are also similar to Typescript's [conditional types](https://github.com/Microsoft/TypeScript/pull/21316). The main differences here are:
240+
Match types are also similar to Typescript's
241+
[conditional types](https://github.com/Microsoft/TypeScript/pull/21316). The
242+
main differences here are:
196243

197-
- Conditional types only reduce if scrutinee and pattern are ground, whereas
198-
match types also work for type parameters and abstract types.
244+
- Conditional types only reduce if both the scrutinee and pattern are ground,
245+
whereas match types also work for type parameters and abstract types.
199246
- Match types can bind variables in type patterns.
200247
- Match types support direct recursion.
201248
- Conditional types distribute through union types.
249+

0 commit comments

Comments
 (0)