导入给定值
一种特殊形式的导入通配符选择器用于导入给定实例。示例
object A:
class TC
given tc: TC = ???
def f(using TC) = ???
object B:
import A.*
import A.given
...
在上面的代码中,对象 B
中的 import A.*
子句导入 A
的所有成员,除了给定实例 tc
。相反,第二个导入 import A.given
将仅导入该给定实例。这两个导入子句也可以合并为一个
object B:
import A.{given, *}
...
通常,普通通配符选择器 *
会将给定值或扩展之外的所有定义引入范围,而 given
选择器会将所有给定值(包括由扩展产生的给定值)引入范围。
这些规则产生了两个主要好处
- 明确了作用域中给定的来源。特别是,无法在常规通配符导入的长列表中隐藏导入的给定。
- 它允许导入所有给定,而无需导入任何其他内容。这一点尤其重要,因为给定可能是匿名的,因此通常使用命名导入的办法并不实用。
按类型导入
由于给定可能是匿名的,因此按名称导入它们并不总是实用,而通常使用通配符导入。按类型导入提供了比通配符导入更具体的替代方案,这使得导入的内容更加清晰。示例
import A.given TC
这将导入 A
中的任何给定,其类型符合 TC
。通过多个 given
选择器来导入多种类型 T1,...,Tn
的给定。
import A.{given T1, ..., given Tn}
通过通配符参数导入给定类型的给定实例。例如,假设对象
object Instances:
given intOrd: Ordering[Int] = ...
given listOrd[T: Ordering]: Ordering[List[T]] = ...
given ec: ExecutionContext = ...
given im: Monoid[Int] = ...
导入子句
import Instances.{given Ordering[?], given ExecutionContext}
将导入 intOrd
、listOrd
和 ec
实例,但会忽略 im
实例,因为它不符合任何指定的上限。
按类型导入可以与按名称导入混合。如果两者都出现在导入子句中,则按类型导入最后出现。例如,导入子句
import Instances.{im, given Ordering[?]}
将导入 im
、intOrd
和 listOrd
,但会忽略 ec
。
迁移
上面陈述的导入规则导致库必须与所有用户一起从旧式隐式导入和常规导入迁移到给定导入和给定。
以下修改避免了迁移的这一障碍。
-
given
导入选择器还将旧式隐式导入作用域。因此,在 Scala 3.0 中,可以通过*
或given
通配符选择器将旧式隐式定义导入作用域。 -
在 Scala 3.1 中,通过
*
通配符导入访问的旧式隐式导入将发出弃用警告。 -
在 3.1 之后的某个版本中,通过
*
通配符导入访问的旧式隐式导入将导致编译器错误。
这些规则意味着库用户可以在 Scala 3.0 中使用 given
选择器访问旧式隐式导入,并且将在以后的版本中被轻柔地推动,然后被迫这样做。然后,一旦用户群迁移,库就可以切换到给定实例。
语法
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}