Importing Givens
A special form of import wildcard selector is used to import given instances. Example:
object A:
class TC
given tc: TC = ???
def f(using TC) = ???
object B:
import A.*
import A.given
...
In the code above, the import A.*
clause in object B
imports all members of A
except the given instance tc
. Conversely, the second import import A.given
will import only that given instance. The two import clauses can also be merged into one:
object B:
import A.{given, *}
...
Generally, a normal wildcard selector *
brings all definitions other than givens or extensions into scope whereas a given
selector brings all givens (including those resulting from extensions) into scope.
There are two main benefits arising from these rules:
- It is made clearer where givens in scope are coming from. In particular, it is not possible to hide imported givens in a long list of regular wildcard imports.
- It enables importing all givens without importing anything else. This is particularly important since givens can be anonymous, so the usual recourse of using named imports is not practical.
Importing By Type
Since givens can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example:
import A.given TC
This imports any given in A
that has a type which conforms to TC
. Importing givens of several types T1,...,Tn
is expressed by multiple given
selectors.
import A.{given T1, ..., given Tn}
Importing all given instances of a parameterized type is expressed by wildcard arguments. For instance, assuming the object
object Instances:
given intOrd: Ordering[Int] = ...
given listOrd: [T: Ordering] => Ordering[List[T]] = ...
given ec: ExecutionContext = ...
given im: Monoid[Int] = ...
the import clause
import Instances.{given Ordering[?], given ExecutionContext}
would import the intOrd
, listOrd
, and ec
instances but leave out the im
instance, since it fits none of the specified bounds.
By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, the import clause
import Instances.{im, given Ordering[?]}
would import im
, intOrd
, and listOrd
but leave out ec
.
Migration
The rules for imports stated above have the consequence that a library would have to migrate in lockstep with all its users from old style implicits and normal imports to givens and given imports.
The following modifications avoid this hurdle to migration.
-
A
given
import selector also brings old style implicits into scope. So, in Scala 3.0 an old-style implicit definition can be brought into scope either by a*
or agiven
wildcard selector. -
In Scala 3.1, old-style implicits accessed through a
*
wildcard import will give a deprecation warning. -
In some version after 3.1, old-style implicits accessed through a
*
wildcard import will give a compiler error.
These rules mean that library users can use given
selectors to access old-style implicits in Scala 3.0, and will be gently nudged and then forced to do so in later versions. Libraries can then switch to given instances once their user base has migrated.
Syntax
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
Export ::= ‘export’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
ImportSpec ::= NamedSelector
| WildcardSelector
| ‘{’ ImportSelectors) ‘}’
NamedSelector ::= id [‘as’ (id | ‘_’)]
WildCardSelector ::= ‘*' | ‘given’ [InfixType]
ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildCardSelector}