You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/reference/match-types.md
+51-2Lines changed: 51 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -183,13 +183,62 @@ A complete defininition of when two patterns or types overlap still needs to be
183
183
The last rule in particular is needed to detect non-overlaps for cases where the scrutinee and the patterns are tuples. I.e. `(Int, String)` does not overlap `(Int, Int)` since
184
184
`String` does not overlap `Int`.
185
185
186
+
## Handling Termination
187
+
188
+
Match type definitions can be recursive, which raises the question whether and how to check
189
+
that reduction terminates. This is currently an open question. We should investigate whether
190
+
there are workable ways to enforce that recursion is primitive.
191
+
192
+
Note that, since reduction is linked to subtyping, we already have a cycle dectection mechanism in place.
193
+
So the following will already give a reasonable error message:
194
+
```scala
195
+
typeL[X] =Xmatch {
196
+
caseInt=>L[X]
197
+
}
198
+
defg[X]:L[X] =???
199
+
```
200
+
201
+
```
202
+
| val x: Int = g[Int]
203
+
| ^^^^^^
204
+
| found: Test.L[Int]
205
+
| required: Int
206
+
```
207
+
208
+
The subtype cycle test can be circumvented by producing larger types in each recursive invocation, as in the following definitions:
209
+
```scala
210
+
typeLL[X] =Xmatch {
211
+
caseInt=>LL[LL[X]]
212
+
}
213
+
defgg[X]:LL[X] =???
214
+
```
215
+
In this case subtyping enters into an infinite recursion. This is not as bad as it looks, however, because
216
+
`dotc` turns selected stack overflows into type errors. If there is a stackoverflow during subtyping,
217
+
the exception will be caught and turned into a compile-time error that indicates
218
+
a trace of the subtype tests that caused the overflow without showing a full stacktrace.
219
+
Concretely:
220
+
```
221
+
| val xx: Int = gg[Int]
222
+
| ^
223
+
|Recursion limit exceeded.
224
+
|Maybe there is an illegal cyclic reference?
225
+
|If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
226
+
|A recurring operation is (inner to outer):
227
+
|
228
+
| subtype Test.LL[Int] <:< Int
229
+
| subtype Test.LL[Int] <:< Int
230
+
| ...
231
+
| subtype Test.LL[Int] <:< Int
232
+
```
233
+
(The actual error message shows some additional lines in the stacktrace).
234
+
186
235
## Related Work
187
236
188
237
Match types have similarities with [closed type families](https://wiki.haskell.org/GHC/Type_families) in Haskell. Some differences are:
189
238
190
239
- Subtyping instead of type equalities.
191
240
- 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.
192
-
- No a-priory requirement that cases are non-overlapping. Uses parallel reduction
241
+
- No a-priori requirement that cases are non-overlapping. Uses parallel reduction
193
242
instead of always chosing a unique branch.
194
243
195
244
Match types are also similar to Typescript's [conditional types](https://github.com/Microsoft/TypeScript/pull/21316). The main differences here are:
@@ -199,6 +248,6 @@ Match types are also similar to Typescript's [conditional types](https://github.
199
248
- Match types can bind variables in type patterns.
200
249
- Match types support direct recursion.
201
250
202
-
Conditional types on Typescript distribute through union types. We should evaluate whether match types should support this as well.
251
+
Conditional types in Typescript distribute through union types. We should evaluate whether match types should support this as well.
0 commit comments