Skip to content

Commit fd42c36

Browse files
committed
Deduplicate accidental duplication
1 parent 0610d5e commit fd42c36

File tree

1 file changed

+15
-87
lines changed

1 file changed

+15
-87
lines changed

_posts/2024-08-19-given-priority-change-3.7.md

Lines changed: 15 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -60,86 +60,10 @@ inheritance patterns more predictable.
6060

6161
### Source Incompatibility of the New Givens Prioritization
6262

63-
Unfortunately, this change might affect source compatibility of some Scala codebases.
64-
Let's consider a library that provides a default
65-
`given` for a component:
66-
```scala
67-
// library code
68-
class LibComponent:
69-
def msg = "library-defined"
70-
71-
// default provided by library
72-
given libComponent: LibComponent = LibComponent()
73-
74-
def printComponent(using c:LibComponent) = println(c.msg)
75-
```
76-
77-
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-
class UserComponent extends LibComponent:
83-
override def msg = "user-defined"
84-
85-
given userComponent: UserComponent = UserComponent()
86-
87-
@main def run = 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-
trait Functor[F[_]]:
114-
extension [A, B](x: F[A]) def map(f: A => B): F[B]
115-
trait Monad[F[_]] extends Functor[F] { ... }
116-
trait Traverse[F[_]] extends Functor[F] { ... }
117-
```
118-
119-
Now, suppose we have corresponding instances of these typeclasses for `List`:
120-
121-
```scala
122-
given Functor[List] = ...
123-
given Monad[List] = ...
124-
given Traverse[List] = ...
125-
```
126-
127-
Let’s use these in the following context:
128-
129-
```scala
130-
def fmap[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:
14367

14468
```scala
14569
// library code
@@ -152,7 +76,8 @@ given libComponent: LibComponent = LibComponent()
15276
def printComponent(using c: LibComponent) = println(c.msg)
15377
```
15478

155-
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:
15681

15782
```scala
15883
// client code
@@ -171,7 +96,10 @@ run // Scala <= 3.6: prints "user-defined"
17196
// Scala 3.7: prints "library-defined"
17297
```
17398

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`).
175103

176104
This shift in prioritization can lead to unexpected changes in
177105
behavior when migrating to Scala 3.7, requiring developers to review
@@ -186,7 +114,7 @@ with the migration process.
186114
We have conducted experiments on the [open community
187115
build](https://github.com/VirtusLab/community-build3) that showed that
188116
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
190118
small. We have tested 1500 open-source libraries, and new rules are
191119
causing problems for less than a dozen of them.
192120

@@ -225,7 +153,7 @@ In Scala 3.6, these warnings will be on by default.
225153

226154
**Scala 3.7**
227155

228-
Scala 3.7 will finalize the transition, making the new given
156+
Scala 3.7 will finalize the transition, making the new `given`
229157
prioritization the standard behavior.
230158

231159
#### Suppressing Warnings
@@ -267,12 +195,12 @@ upgrading to future versions of the Scala compiler.
267195
### Workarounds
268196

269197
Here are some practical strategies to help you smoothly adapt to the
270-
new given resolution scheme:
198+
new `given` resolution scheme:
271199

272200
#### Resorting to Explicit Parameters
273201

274202
If the pre-3.7 behavior is preferred, you can explicitly pass the
275-
desired given:
203+
desired `given`:
276204
```scala
277205
@main def run = printComponent(using userComponent)
278206
```
@@ -292,7 +220,7 @@ This will output all parameters explicitly:
292220

293221
#### Explicit Prioritization by Owner
294222

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
296224
selected -— particularly useful when migrating libraries to Scala 3.7 -—
297225
is to leverage the inheritance rules as outlined in point 8 of [the
298226
language

0 commit comments

Comments
 (0)