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
@@ -7,10 +7,61 @@ title: "Upcoming Changes to Givens in Scala 3.7"
7
7
8
8
## New Prioritization of Givens in Scala 3.7
9
9
10
-
Scala 3.7 will introduce changes to how `given`s are resolved, which can
11
-
affect program behavior when multiple `given`s are present.
10
+
Scala 3.7 will introduce changes to how `given`s are resolved, which
11
+
may affect program behavior when multiple `given`s are present. The
12
+
aim of this change is to make `given` resolution more predictable, but
13
+
it could lead to problems during migration to Scala 3.7 or later
14
+
versions. In this article, we’ll explore the motivation behind these
15
+
changes, potential issues, and provide migration guides to help
16
+
developers prepare for the transition.
12
17
13
-
For example, consider a library that provides a default
18
+
### Motivation: Better Handling of Inheritance Triangles & Typeclasses
19
+
20
+
The motivation for changing the prioritization of `given`s stems from
21
+
the need to make interactions within inheritance hierarchies,
22
+
particularly inheritance triangles, more intuitive. This adjustment
23
+
addresses a common issue where the compiler struggles with ambiguity
24
+
in complex typeclass hierarchies.
25
+
26
+
For example, functional programmers will recognize the following
27
+
inheritance triangle of common typeclasses:
28
+
29
+
```scala
30
+
traitFunctor[F[_]]:
31
+
extension [A, B](x: F[A]) defmap(f: A=>B):F[B]
32
+
traitMonad[F[_]] extendsFunctor[F] { ... }
33
+
traitTraverse[F[_]] extendsFunctor[F] { ... }
34
+
```
35
+
Now, suppose we have corresponding instances of these typeclasses for `List`:
36
+
```scala
37
+
givenFunctor[List] = ...
38
+
givenMonad[List] = ...
39
+
givenTraverse[List] = ...
40
+
```
41
+
Let’s use these in the following context:
42
+
```scala
43
+
deffmap[F[_] :Functor, A, B](c: F[A])(f: A=>B):F[B] = c.map(f)
44
+
45
+
fmap(List(1,2,3))(_.toString)
46
+
// ^ rejected by Scala < 3.7, now accepted by Scala 3.7
47
+
```
48
+
49
+
Before Scala 3.7, the compiler would reject the `fmap` call due to
50
+
ambiguity. Since it prioritized the `given` instance with the _most
51
+
specific_ subtype of the context bound `Functor`, both `Monad[List]`
52
+
and `Traverse[List]` were valid candidates for `Functor[List]`, but
53
+
neither was more specific than the other. However, all that’s required
54
+
is the functionality of `Functor[List]`, the instance with the _most
55
+
general_ subtype, which Scala 3.7 correctly picks.
56
+
57
+
This change aligns the behavior of the compiler with the practical
58
+
needs of developers, making the handling of common triangle
59
+
inheritance patterns more predictable.
60
+
61
+
### Source Incompatibility of the New Givens Prioritization
62
+
63
+
Unfortunately, this change might affect source compatibility of some Scala codebases.
64
+
Let's consider a library that provides a default
14
65
`given` for a component:
15
66
```scala
16
67
// library code
@@ -48,47 +99,87 @@ What happened? In Scala 3.6 and earlier, the compiler prioritizes the
48
99
(`userComponent`). However, in Scala 3.7, it selects the value with the
49
100
_most general_ subtype instead (`libComponent`).
50
101
102
+
Here's the revised version with improved transitions and adjustments for the reordering:
103
+
104
+
## New Prioritization of Givens in Scala 3.7
105
+
106
+
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.
51
107
52
108
### Motivation: Better Handling of Inheritance Triangles & Typeclasses
53
109
54
-
Why change the priority to the `given` with the most general subtype?
55
-
This adjustment makes working with inheritance triangles more
56
-
intuitive.
110
+
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.
57
111
58
-
For example, functional programmers will recognize the following
59
-
inheritance triangle of common typeclasses:
112
+
Consider the following common inheritance triangle in functional programming:
60
113
61
114
```scala
62
115
traitFunctor[F[_]]:
63
116
extension [A, B](x: F[A]) defmap(f: A=>B):F[B]
64
117
traitMonad[F[_]] extendsFunctor[F] { ... }
65
118
traitTraverse[F[_]] extendsFunctor[F] { ... }
66
119
```
120
+
67
121
Now, suppose we have corresponding instances of these typeclasses for `List`:
122
+
68
123
```scala
69
124
givenFunctor[List] = ...
70
125
givenMonad[List] = ...
71
126
givenTraverse[List] = ...
72
127
```
128
+
73
129
Let’s use these in the following context:
130
+
74
131
```scala
75
132
deffmap[F[_] :Functor, A, B](c: F[A])(f: A=>B):F[B] = c.map(f)
76
133
77
134
fmap(List(1,2,3))(_.toString)
78
135
// ^ rejected by Scala < 3.7, now accepted by Scala 3.7
79
136
```
80
137
81
-
Before Scala 3.7, the compiler would reject the `fmap` call due to
82
-
ambiguity. Since it prioritized the `given` instance with the most
83
-
specific subtype of the context bound `Functor`, both `Monad[List]` and
84
-
`Traverse[List]` were valid candidates for `Functor[List]`, but neither
85
-
was more specific than the other. However, all that’s required is the
86
-
functionality of `Functor[List]`, the _most general_ instance, which Scala
87
-
3.7 correctly picks.
138
+
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.
88
139
89
-
This change aligns the behavior of the compiler with the practical
90
-
needs of developers, making the handling of common triangle inheritance
91
-
patterns more predictable.
140
+
This change aligns compiler behavior with developers' expectations, making it easier to work with common inheritance patterns without encountering unnecessary ambiguity.
141
+
142
+
### Source Incompatibility of the New Givens Prioritization
143
+
144
+
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:
Up until Scala 3.6, clients of the library could override `libComponent` with a user-defined one through subtyping:
158
+
159
+
```scala
160
+
// client code
161
+
classUserComponentextendsLibComponent:
162
+
overridedefmsg="user-defined"
163
+
164
+
givenuserComponent:UserComponent=UserComponent()
165
+
166
+
@main defrun= printComponent
167
+
```
168
+
169
+
Now, let’s run the example:
170
+
171
+
```scala
172
+
run // Scala <= 3.6: prints "user-defined"
173
+
// Scala 3.7: prints "library-defined"
174
+
```
175
+
176
+
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`).
177
+
178
+
This shift in prioritization can lead to unexpected changes in
179
+
behavior when migrating to Scala 3.7, requiring developers to review
180
+
and potentially adjust their codebases to ensure compatibility with
181
+
the new `given` resolution logic. Below, we provide some tips to help
0 commit comments