And*_*kin 5 scala implicit initialization-order scala-3 given
假设我有一些类型类
trait FooBar[X]
Run Code Online (Sandbox Code Playgroud)
和一个实例FooBar[Int]:
given intIsFooBar: FooBar[Int] = new FooBar {}
Run Code Online (Sandbox Code Playgroud)
现在,假设我有一个Intf具有某种成员类型的接口A,并且还保证有一个given FooBar[A]:
trait Intf:
type A
given aIsFoobar: FooBar[A]
Run Code Online (Sandbox Code Playgroud)
现在,我有了类型Int,也有一个FooBar[Int],但是我如何实际实现这个接口呢Int?
如果我尝试
class IntImpl() extends Intf:
type A = Int
given aIsFoobar: FooBar[A] = summon
Run Code Online (Sandbox Code Playgroud)
然后我收到“函数体 IntImpl.aIsFoobar 中的无限循环”错误,因为summon似乎看到的是aIsFoobar而不是intIsFooBar。
如果我尝试使用summon某些辅助辅助变量中的实例,如下所示:
class IntImpl() extends Intf:
type A = Int
private final val _aIsFoobar: FooBar[A] = summon
given aIsFoobar: FooBar[A] = _aIsFoobar
Run Code Online (Sandbox Code Playgroud)
然后我遇到了初始化顺序问题:aIsFoobar结果是null,我的应用程序崩溃了NullPointerExceptions,这有点荒谬。
我也尝试过export,但这些都不起作用:
export FooBar[Int] as aIsFoobar // doesn't compile, invalid syntax
Run Code Online (Sandbox Code Playgroud)
如何使“规范”FooBar[Int]作为aIsFoobar给定成员可用?
完整代码:
trait FooBar[X]
given intIsFooBar: FooBar[Int] = new FooBar {}
trait Intf:
type A
given aIsFoobar: FooBar[A]
object IntImpl extends Intf:
type A = Int
given aIsFoobar: FooBar[A] = summon
Run Code Online (Sandbox Code Playgroud)
在 Scala 2 中,您可以使用按名称隐藏隐式的技巧
// Scala 2
trait FooBar[X] {
def value: String
}
object FooBar {
implicit val intIsFooBar: FooBar[Int] = new FooBar[Int] {
override val value: String = "a"
}
}
trait Intf {
type A
implicit def aIsFoobar: FooBar[A]
}
object IntImpl extends Intf {
override type A = Int
override implicit val aIsFoobar: FooBar[A] = {
lazy val aIsFoobar = ???
implicitly[FooBar[A]]
}
}
println(IntImpl.aIsFoobar.value) // a
Run Code Online (Sandbox Code Playgroud)
在 Scala 3 中,使用擦除类型的模式匹配的规范方法是什么?
在 Scala 3 中这个技巧不再起作用了。
在 Scala 3 中,您可以尝试使该方法内联并使用,scala.compiletime.summonInline而不是普通的summon
// Scala 3
trait FooBar[X]:
def value: String
object FooBar:
given intIsFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "a"
trait Intf:
type A
/*inline*/ given aIsFoobar: FooBar[A]
object IntImpl extends Intf:
override type A = Int
override inline given aIsFoobar: FooBar[A] = summonInline[FooBar[A]]
println(IntImpl.aIsFoobar.value) // a
Run Code Online (Sandbox Code Playgroud)
重写内联方法:https://docs.scala-lang.org/scala3/reference/metaprogramming/inline.html#rules-for-overriding
请注意,通过内联,我们修改了方法语义。隐式在调用站点而不是定义站点解析
// Scala 2
trait FooBar[X] {
def value: String
}
object FooBar {
implicit val intIsFooBar: FooBar[Int] = new FooBar[Int] {
override val value: String = "a"
}
}
trait Intf {
type A
implicit def aIsFoobar: FooBar[A]
}
object IntImpl extends Intf {
override type A = Int
override implicit val aIsFoobar: FooBar[A] = {
lazy val aIsFoobar = ???
implicitly[FooBar[A]]
}
}
{
implicit val anotherIntFooBar: FooBar[Int] = new FooBar[Int] {
override val value: String = "b"
}
println(IntImpl.aIsFoobar.value) // a
}
Run Code Online (Sandbox Code Playgroud)
// Scala 3
trait FooBar[X]:
def value: String
object FooBar:
given intIsFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "a"
trait Intf:
type A
/*inline*/ given aIsFoobar: FooBar[A]
object IntImpl extends Intf:
override type A = Int
override inline given aIsFoobar: FooBar[A] = summonInline[FooBar[A]]
{
given anotherIntFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "b"
println(IntImpl.aIsFoobar.value) // b
}
Run Code Online (Sandbox Code Playgroud)
implicitly关于与 的区别implicit:
为什么Scala编译器可以提供隐式的out of object,但不能提供inside?(回答)
在scala 2中,是否可以使用宏或任何语言特性来重写所有子类中的抽象类型具体化机制?斯卡拉 3 怎么样?
在Scala 2.13中,为什么可以为抽象类型调用不合格的TypeTag?
在 Scala 2 中,内联可以通过Scala 2 宏来实现。
Scala 2 中的抽象隐式 val 或 def 可以在 Scala 3 中使用常规抽象定义和给定的别名来表达。例如,Scala 2
Run Code Online (Sandbox Code Playgroud)implicit def symDecorator: SymDecorator在 Scala 3 中可以表示为
Run Code Online (Sandbox Code Playgroud)def symDecorator: SymDecorator given SymDecorator = symDecorator
您可以询问如何在 Scala 3 中重写隐式而不更改定义站点语义。可能只是手动解决隐式问题而不是使用summon
// Scala 3
trait FooBar[X]:
def value: String
object FooBar:
given intIsFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "a"
trait Intf:
type A
def aIsFoobar: FooBar[A]
given FooBar[A] = aIsFoobar
object IntImpl extends Intf:
override type A = Int
override val aIsFoobar: FooBar[A] = FooBar.intIsFooBar
{
given anotherIntFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "b"
println(IntImpl.aIsFoobar.value) // a
}
Run Code Online (Sandbox Code Playgroud)
更通用但不太传统的解决方案是使用Scala 3 宏+ 编译器内部结构
// Scala 3.2.1
import scala.quoted.{Quotes, Type, Expr, quotes}
import dotty.tools.dotc.typer.{Implicits => dottyImplicits}
import dotty.tools.dotc.core.Types.{Type => DottyType}
transparent inline def summonSecondBest[A]: A = ${summonSecondBestImpl[A]}
def summonSecondBestImpl[A: Type](using Quotes): Expr[A] =
import quotes.reflect.*
given c: dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
val typer = c.typer
val search = new typer.ImplicitSearch(
TypeRepr.of[A].asInstanceOf[DottyType],
dotty.tools.dotc.ast.tpd.EmptyTree,
Position.ofMacroExpansion.asInstanceOf[dotty.tools.dotc.util.SourcePosition].span
)
val wildProtoMethod = classOf[typer.ImplicitSearch].getDeclaredField("wildProto")
wildProtoMethod.setAccessible(true)
val wildProto = wildProtoMethod.get(search).asInstanceOf[DottyType]
def eligible(contextual: Boolean): List[dottyImplicits.Candidate] =
if contextual then
if c.gadt.isNarrowing then
dotty.tools.dotc.core.Contexts.withoutMode(dotty.tools.dotc.core.Mode.ImplicitsEnabled) {
c.implicits.uncachedEligible(wildProto)
}
else c.implicits.eligible(wildProto)
else search.implicitScope(wildProto).eligible
def implicits(contextual: Boolean): List[dottyImplicits.SearchResult] =
eligible(contextual).map(search.tryImplicit(_, contextual))
val contextualImplicits = implicits(true)
val nonContextualImplicits = implicits(false)
val contextualSymbols = contextualImplicits.map(_.tree.symbol)
val filteredNonContextual = nonContextualImplicits.filterNot(sr => contextualSymbols.contains(sr.tree.symbol))
val successes = (contextualImplicits ++ filteredNonContextual).collect {
case success: dottyImplicits.SearchSuccess => success.tree.asInstanceOf[ImplicitSearchSuccess].tree
}
successes.tail.head.asExprOf[A]
Run Code Online (Sandbox Code Playgroud)
// Scala 3
trait FooBar[X]:
def value: String
object FooBar:
given intIsFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "a"
trait Intf:
type A
def aIsFoobar: FooBar[A]
given FooBar[A] = aIsFoobar
object IntImpl extends Intf:
override type A = Int
override val aIsFoobar: FooBar[A] = summonSecondBest[FooBar[A]]
{
given anotherIntFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "b"
println(IntImpl.aIsFoobar.value) // a
}
Run Code Online (Sandbox Code Playgroud)
或者您可以尝试创建A类型参数而不是类型成员
trait FooBar[X]
object FooBar:
given FooBar[Int] with {}
trait Intf[A: FooBar]
object IntImpl extends Intf[Int]
Run Code Online (Sandbox Code Playgroud)
https://docs.scala-lang.org/scala3/reference/changed-features/implicit-resolution.html
现在选择隐式时会考虑嵌套。例如考虑以下场景:
Run Code Online (Sandbox Code Playgroud)def f(implicit i: C) = def g(implicit j: C) = implicitly[C]现在,这将解决对 的隐式调用
j,因为j嵌套得比 更深i。以前,这会导致歧义错误。以前由于隐藏(其中隐式被嵌套定义隐藏)而导致隐式搜索失败的可能性不再适用。
@AndreyTyukin 的解决方案:
trait FooBar[X]:
def value: String
object FooBar:
given intIsFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "a"
trait Intf:
type A
def aIsFoobar: FooBar[A]
object implicits:
given FooBar[A] = aIsFoobar
object IntImpl extends Intf:
override type A = Int
override def aIsFoobar: FooBar[A] = summon[FooBar[Int]]
{
given anotherIntFooBar: FooBar[Int] = new FooBar[Int]:
override val value: String = "b"
println(IntImpl.aIsFoobar.value) // a
}
{
import IntImpl.implicits.given
println(summon[FooBar[Int]].value) // a
}
Run Code Online (Sandbox Code Playgroud)