Skip to content

Commit 67fc9a0

Browse files
committed
Update blog post
* Clarify compiler behavior and compiler options * Add discussion on changes to context bounds
1 parent 66dc2a7 commit 67fc9a0

File tree

1 file changed

+77
-13
lines changed

1 file changed

+77
-13
lines changed

_posts/2024-08-01-given-priority-change-3.5.md

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
layout: blog-detail
33
post-type: blog
44
by: Oliver Bračevac, EPFL
5-
title: "Changes to Givens Prioritization in Scala 3.5"
5+
title: "Changes to Givens in Scala 3.5"
66
---
77

8-
## Motivation
8+
## New Prioritization of Givens
99

1010
Starting with Scala 3.5, the prioritization of givens has been
1111
improved to better handle inheritance triangles, resulting in enhanced
1212
typeclass support.
1313

14-
Consider a scenario with the following inheritance triangle of type classes:
14+
Consider a scenario with the following inheritance triangle of type
15+
classes:
1516
```scala
1617
trait Functor[F[_]]:
1718
extension [A, B](x: F[A]) def map(f: A => B): F[B]
@@ -40,19 +41,24 @@ than the other. However, all we really need is the functionality of
4041

4142
In Scala 3.5, the compiler now selects the instance with the _most
4243
general_ subtype that satisfies the context bound of `fmap`. In this
43-
case, it chooses `a:Functor[List]`.
44+
case, it chooses the desired `a:Functor[List]`.
4445

4546
Inheritance triangles like this are common in practice, and the
4647
prioritization change in Scala 3.5 makes working with them more
4748
intuitive and straightforward.
4849

49-
## Tips for Migrating to 3.5
50+
### Community Impact
5051

5152
Based on our evaluation using the [open community
5253
build](https://github.com/VirtusLab/community-build3), the impact of
5354
this change on existing Scala 3 projects has been minimal. However,
5455
there may still be cases where the behavior of existing programs
55-
changes due to the new prioritization of givens.
56+
changes due to the new prioritization of givens. Cf. below for
57+
tips to migrate to Scala 3.5.
58+
59+
60+
## Tips for Migrating to the New Prioritization
61+
5662

5763
In some cases, the new prioritization might silently select the wrong
5864
`given`. For example, consider a library that provides a default
@@ -82,12 +88,9 @@ given userComponent: UserComponent = UserComponent()
8288
// Scala 3.5: prints "library-defined"
8389
```
8490

85-
To detect such "silent" changes, we recommend compiling under Scala
86-
3.5 with the `-source:3.6-migration` flag:
87-
```bash
88-
scalac client.scala -source:3.6-migration
89-
```
90-
This will issue warnings when the choice of `given` has changed:
91+
Scala 3.5 will automatically issue
92+
warnings when the choice of `given` has changed:
93+
9194
```scala
9295
-- Warning: client.scala:11:30 ------------------------------------------
9396
11 |@main def run = printComponent
@@ -101,7 +104,20 @@ This will issue warnings when the choice of `given` has changed:
101104
| New choice from Scala 3.6: the second alternative
102105
```
103106

104-
### Explicit Parameters
107+
108+
### Useful Compiler Options
109+
110+
In future releases (Scala 3.6+), automatic warnings related to changes
111+
in the selection of givens, as described above, will no longer be
112+
issued by default. However, these warnings can be reactivated using
113+
the `-source:3.5` option with `scalac`.
114+
115+
Additionally, combining Scala 3.5 with the `-source:3.6` option can be
116+
useful to verify that implicit search results will not be ambiguous in
117+
future versions or to test your application at runtime with the new
118+
rules in effect.
119+
120+
### Resorting to Explicit Parameters
105121

106122
If the pre-3.5 behavior is preferred, you can explicitly pass the
107123
desired given:
@@ -126,3 +142,51 @@ This will output all parameters explicitly:
126142

127143
We are considering adding `-rewrite` rules that automatically insert
128144
explicit parameters when a change in choice is detected.
145+
146+
147+
## Towards Context Bounds as Givens
148+
149+
We are gradually phasing out remaining uses of Scala 2 style
150+
`implicit`s in favor of the `given`/`using` syntax. Scala 3.5 marks
151+
the first step in transitioning context bounds on type parameters to
152+
givens, with this transition expected to be completed in the upcoming
153+
Scala 3.6 release.
154+
155+
Currently, context bounds on type parameters still desugar into
156+
`implicit` parameters:
157+
158+
```scala
159+
def f[Element : Eq : Ordering] = summon[Eq[Element]].toOrdering
160+
// expands to:
161+
def f[Element >: Nothing <: Any](implicit evidence$1: Eq[Element],
162+
implicit evidence$2: Order[Element]): Ordering[Element] =
163+
evidence$2.toOrdering
164+
```
165+
166+
Prior to Scala 3.5, it was possible to pass `implicit` arguments
167+
explicitly for context bounds as if they were regular arguments. In
168+
Scala 3.5, however, these parameters must be qualified with `using`:
169+
170+
```scala
171+
val eq: Eq[Int] = ???
172+
val ord: Order[Int] = ???
173+
f(eq, ord) // ok in Scala < 3.5, error in 3.5
174+
f(using eq, ord) // ok in Scala 3.5
175+
```
176+
177+
At this stage, the change does not affect the expansion of functions
178+
like `f` above, which still rely on `implicit` parameters. However,
179+
this is a crucial step towards facilitating the eventual transition to
180+
`given`s for context bounds in Scala 3.6.
181+
182+
To assist with the migration to explicit `using` clauses, Scala 3.5
183+
provides an error message and offers automatic rewrites:
184+
185+
```scala
186+
-- Error: bounds.scala:10:2 ----------------------------------------------
187+
10 | f(eq, ord) // error
188+
| ^
189+
|Context bounds will map to context parameters.
190+
|A `using` clause is needed to pass explicit arguments to them.
191+
|This code can be rewritten automatically under -rewrite -source 3.4-migration.
192+
```

0 commit comments

Comments
 (0)