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
Scala is a language in the SML tradition, in the sense that it has
12
-
abstract and alias types as members of classes. This leads to a simple dependently
12
+
abstract and alias types as members of modules (which in Scala take the form of objects and classes). This leads to a simple dependently
13
13
typed system, where dependencies in types are on paths instead of full terms.
14
14
15
15
So far, some key ingredients were lacking which meant that module composition with functors is harder in Scala than in SML. In particular, one often needs to resort the infamous `Aux` pattern that lifts type members into type parameters so that they can be tracked across class instantiations. This makes modular, dependently typed programs
@@ -19,10 +19,10 @@ In this note I propose some small changes to Scala's dependent typing that makes
19
19
modular programming much more straightforward.
20
20
21
21
The suggested improvements have been implemented and are available
22
-
in source version `future` if the additional experimental language import `modularity` is present. For instance, using
22
+
in source version `future` if the additional experimental language import `modularity` is present. For instance, using the following command:
@@ -41,7 +41,7 @@ For instance, consider the following definitions:
41
41
```
42
42
Then `f(y)` would have type `Int`, since the compiler will substitute the
43
43
concrete parameter reference `y` for the formal parameter `x` in the result
44
-
type of `f`.
44
+
type of `f`, and `y.T = Int`
45
45
46
46
However, if we use a class `F` instead of a method `f`, things go wrong.
47
47
@@ -66,7 +66,7 @@ Then the constructor `F` would get roughly the following type:
66
66
```scala
67
67
F(x1: C):F { valx: x1.type }
68
68
```
69
-
_Aside:_ More precisely, both parameter and refinement would apply to the same name `x` but the refinement still refers to the parameter. We unfortunately can't express that in source, however, so we chose the new name `x1` for the parameter.
69
+
_Aside:_ More precisely, both parameter and refinement would apply to the same name `x` but the refinement still refers to the parameter. We unfortunately can't express that in source, however, so we chose the new name `x1` for the parameter in the explanation.
70
70
71
71
With the new constructor type, the expression `F(y).result` would now have the type `Int`, as hoped for. The reasoning to get there is as follows:
72
72
@@ -118,23 +118,21 @@ The (soft) `tracked` modifier is only allowed for `val` parameters of classes.
118
118
119
119
**Discussion**
120
120
121
-
Since `tracked` is so useful, why not assume it by default? First, `tracked` makes sense only for `val` parameters. If a class parameter is not also a field declared using `val` then there's nothing to refine in the constructor.
122
-
But making all `val` parameters tracked by default would also be a backwards
123
-
incompatible change. For instance, the following code would break:
121
+
Since `tracked` is so useful, why not assume it by default? First, `tracked` makes sense only for `val` parameters. If a class parameter is not also a field declared using `val` then there's nothing to refine in the constructor result type. One could think of at least making all `val` parameters tracked by default, but that would be a backwards incompatible change. For instance, the following code would break:
124
122
125
123
```scala
126
124
caseclassFoo(x: Int)
127
125
varfoo=Foo(1)
128
126
if someCondition then foo =Foo(2)
129
127
```
130
-
if we assume `tracked` for parameter `x` (which is implicitly a `val`),
128
+
If we assume `tracked` for parameter `x` (which is implicitly a `val`),
131
129
then `foo` would get inferred type `Foo { val x: 1 }`, so it could not
132
-
be reassigned to a value of type `Foo { val x: 2 }`.
130
+
be reassigned to a value of type `Foo { val x: 2 }` on the next line.
133
131
134
132
Another approach might be to assume `tracked` for a `val` parameter `x`
135
133
only if the class refers to a type member of `x`. But it turns out that this
136
134
scheme is unimplementable since it would quickly lead to cyclic references
137
-
in recursive class graphs. So an explicit `tracked` looks like the best feasible option.
135
+
when typechecking recursive class graphs. So an explicit `tracked` looks like the best available option.
138
136
139
137
## Allow Class Parents to be Refined Types
140
138
@@ -186,6 +184,6 @@ The rules for export forwarders are changed as follows.
186
184
187
185
Previously, all export forwarders were declared `final`. Now, only term members are declared `final`. Type aliases are left aside.
188
186
189
-
This makes it possible to export the same type member into several traits and then mix these traits in the same class. `typeclass-aggregates.scala` shows why this is essential to be able to combine multiple givens with type members.
187
+
This makes it possible to export the same type member into several traits and then mix these traits in the same class. The test file `tests/pos/typeclass-aggregates.scala` shows why this is essential if we want to combine multiple givens with type members in a new given that aggregates all these givens in an intersection type.
190
188
191
189
The change does not lose safety since different type aliases would in any case lead to uninstantiatable classes.
0 commit comments