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
Up until Scala 3.6, clients of the library could override
78
-
`libComponent` with a user-defined one through subtyping
79
-
80
-
```scala
81
-
// client code
82
-
classUserComponentextendsLibComponent:
83
-
overridedefmsg="user-defined"
84
-
85
-
givenuserComponent:UserComponent=UserComponent()
86
-
87
-
@main defrun= printComponent
88
-
```
89
-
90
-
Let's run the example:
91
-
92
-
```scala
93
-
run // Scala <= 3.6: prints "user-defined"
94
-
// Scala 3.7: prints "library-defined"
95
-
```
96
-
97
-
What happened? In Scala 3.6 and earlier, the compiler prioritizes the
98
-
`given` with the _most specific_ compatible subtype
99
-
(`userComponent`). However, in Scala 3.7, it selects the value with the
100
-
_most general_ subtype instead (`libComponent`).
101
-
102
-
## New Prioritization of Givens in Scala 3.7
103
-
104
-
Scala 3.7 will introduce changes to how `given`s are resolved, which may affect program behavior when multiple `given`s are present. The aim of this change is to make `given` resolution more predictable, but it could lead to challenges during migration to Scala 3.7 or later versions. In this article, we’ll explore the motivation behind these changes, potential issues, and provide migration guides to help developers prepare for the transition.
105
-
106
-
### Motivation: Better Handling of Inheritance Triangles & Typeclasses
107
-
108
-
The motivation for changing the prioritization of `givens` stems from the need to make interactions within inheritance hierarchies, particularly inheritance triangles, more intuitive. This adjustment addresses a common issue where the compiler struggles with ambiguity in complex typeclass hierarchies.
109
-
110
-
Consider the following common inheritance triangle in functional programming:
111
-
112
-
```scala
113
-
traitFunctor[F[_]]:
114
-
extension [A, B](x: F[A]) defmap(f: A=>B):F[B]
115
-
traitMonad[F[_]] extendsFunctor[F] { ... }
116
-
traitTraverse[F[_]] extendsFunctor[F] { ... }
117
-
```
118
-
119
-
Now, suppose we have corresponding instances of these typeclasses for `List`:
120
-
121
-
```scala
122
-
givenFunctor[List] = ...
123
-
givenMonad[List] = ...
124
-
givenTraverse[List] = ...
125
-
```
126
-
127
-
Let’s use these in the following context:
128
-
129
-
```scala
130
-
deffmap[F[_] :Functor, A, B](c: F[A])(f: A=>B):F[B] = c.map(f)
131
-
132
-
fmap(List(1,2,3))(_.toString)
133
-
// ^ rejected by Scala < 3.7, now accepted by Scala 3.7
134
-
```
135
-
136
-
Before Scala 3.7, the compiler would reject the `fmap` call due to ambiguity. The issue arose because the compiler prioritized the `given` instance with the most specific subtype of the context bound `Functor`. Both `Monad[List]` and `Traverse[List]` were valid candidates for `Functor[List]`, but neither was more specific than the other. However, all that’s required is the functionality of `Functor[List]`, the _most general_ instance, which Scala 3.7 now correctly picks.
137
-
138
-
This change aligns compiler behavior with developers' expectations, making it easier to work with common inheritance patterns without encountering unnecessary ambiguity.
139
-
140
-
### Source Incompatibility of the New Givens Prioritization
141
-
142
-
While the new `given` prioritization improves predictability, it may affect source compatibility in existing Scala codebases. Let’s consider an example where a library provides a default `given` for a component:
63
+
While the new `given` prioritization improves predictability, it may
64
+
affect source compatibility in existing Scala codebases. Let’s
65
+
consider an example where a library provides a default `given` for a
66
+
component:
143
67
144
68
```scala
145
69
// library code
@@ -152,7 +76,8 @@ given libComponent: LibComponent = LibComponent()
Up until Scala 3.6, clients of the library could override `libComponent` with a user-defined one through subtyping:
79
+
Up until Scala 3.6, clients of the library could override
80
+
`libComponent` with a user-defined one through subtyping:
156
81
157
82
```scala
158
83
// client code
@@ -171,7 +96,10 @@ run // Scala <= 3.6: prints "user-defined"
171
96
// Scala 3.7: prints "library-defined"
172
97
```
173
98
174
-
What happened? In Scala 3.6 and earlier, the compiler prioritized the `given` with the _most specific_ compatible subtype (`userComponent`). However, in Scala 3.7, it selects the value with the _most general_ subtype instead (`libComponent`).
99
+
What happened? In Scala 3.6 and earlier, the compiler prioritized the
100
+
`given` with the _most specific_ compatible subtype
101
+
(`userComponent`). However, in Scala 3.7, it selects the value with
102
+
the _most general_ subtype instead (`libComponent`).
175
103
176
104
This shift in prioritization can lead to unexpected changes in
177
105
behavior when migrating to Scala 3.7, requiring developers to review
@@ -186,7 +114,7 @@ with the migration process.
186
114
We have conducted experiments on the [open community
187
115
build](https://github.com/VirtusLab/community-build3) that showed that
188
116
the proposed scheme will result in a more intuitive and predictable
189
-
given resolution. The negative impact on the existing projects is very
117
+
`given` resolution. The negative impact on the existing projects is very
190
118
small. We have tested 1500 open-source libraries, and new rules are
191
119
causing problems for less than a dozen of them.
192
120
@@ -225,7 +153,7 @@ In Scala 3.6, these warnings will be on by default.
225
153
226
154
**Scala 3.7**
227
155
228
-
Scala 3.7 will finalize the transition, making the new given
156
+
Scala 3.7 will finalize the transition, making the new `given`
229
157
prioritization the standard behavior.
230
158
231
159
#### Suppressing Warnings
@@ -267,12 +195,12 @@ upgrading to future versions of the Scala compiler.
267
195
### Workarounds
268
196
269
197
Here are some practical strategies to help you smoothly adapt to the
270
-
new given resolution scheme:
198
+
new `given` resolution scheme:
271
199
272
200
#### Resorting to Explicit Parameters
273
201
274
202
If the pre-3.7 behavior is preferred, you can explicitly pass the
275
-
desired given:
203
+
desired `given`:
276
204
```scala
277
205
@main defrun= printComponent(using userComponent)
278
206
```
@@ -292,7 +220,7 @@ This will output all parameters explicitly:
292
220
293
221
#### Explicit Prioritization by Owner
294
222
295
-
One effective way to ensure that the most specific given instance is
223
+
One effective way to ensure that the most specific `given` instance is
296
224
selected -— particularly useful when migrating libraries to Scala 3.7 -—
297
225
is to leverage the inheritance rules as outlined in point 8 of [the
0 commit comments