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
{{ message }}
This repository was archived by the owner on Mar 27, 2025. It is now read-only.
In the chapters on extension methods, we mentioned abstraction over context
via implicits in Scala 2
via given and using in Scala 3
given instances define a "canonical" value of a certain type that can be used for synthesising arguments for context parameters (using clauses)
The introduction of the new keywords given and using allows us to make a clear distinction between the definition of "canonical" values and the actual use of them
This eliminates the confusion that was caused by the multiple distinctive use cases of the Scala 2 implicit keyword
When moving from Scala 2, we have the benefit to move to Scala 3's Contextual Abstractions in steps
using Clauses and given Instances
Let's start with a trivial example to illustrate the concept
If the name of a given is missing, the compiler will synthesise a name from the implemented type(s). The above instance will become:
lazyvalgiven_EngineConfig:EngineConfig
The synthesised type names use the following rules:
The prefix given_
The simple name(s) of the implemented type(s)
The simple name(s) of the toplevel argument type constructors to these types
using in detail
First of all, some terminology
In the previously used definition:
defstartEngine(engine: Engine)(usingconfig: EngineConfig):Unit=
println(s"Starting $engine with $config")
We have:
A [single] regular parameter list with a single parameter
A [single] using clause with a single context parameter
Scala 3 allows for more than one using clause. Let's have a look at this
Multiple using clauses
recordAndMonitor is a method that has 2 using clauses and 2 regular parameter lists:
finalcaseclassRecordDev(recordType: String)
finalcaseclassPlayerDev(playerType: String)
defrecordAndMonitor(recordGain: Int)
(usingrecordDev: RecordDev)
(volume: Int)
(usingplayer: PlayerDev) =
println(s"Recording with gain $recordGain from $recordDev to $player with volume $volume")
recordAndMonitor can be invoked in different ways:
If you want to explicitly pass an instance of Ord[Int], do it as follows
scala> max(1,3)(using intOrd)
valres5:Int=3
Context bounds in Scala 3 maps to old-style implicit parameters in Scala 3.0 to ease the migration.
From Scala 3.1 onwards, they will map to using clauses instead.
It means, the following will still be allowed in Scala 3.0, but generate a warning in later versions
[warn] --MigrationWarning:Test.scala:29:5
[warn] 29| max(1,3)(intOrd)
[warn] |^^^^^^^^
[warn] |Context bounds will map to context parameters.
[warn] |A `using` clause is needed to pass explicit arguments to them.
[warn] |This code can be rewritten automatically under -rewrite -source future-migration.
Summoning instances - I
The method summon can be used to retrieve the given of a specific type.