scala 如何召唤“指定”成员?

0md85ypi  于 2023-03-23  发布在  Scala
关注(0)|答案(1)|浏览(135)

假设我有一些类型类

trait FooBar[X]

FooBar[Int]的示例:

given intIsFooBar: FooBar[Int] = new FooBar {}

现在,假设我有一个接口Intf,它有一些成员类型A,并且还保证有一个given FooBar[A]

trait Intf:
  type A
  given aIsFoobar: FooBar[A]

现在,我有一个Int类型,还有一个FooBar[Int],但是我如何为Int实现这个接口呢?
如果我尝试

class IntImpl() extends Intf:
  type A = Int
  given aIsFoobar: FooBar[A] = summon

然后我得到 “Infinite loop in function body 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

然后我遇到了初始化顺序问题:aIsFoobar原来是null,我的应用程序崩溃与NullPointerExceptions,这是有点荒谬。
我也试过export,但都不起作用:

export FooBar[Int] as aIsFoobar // doesn't compile, invalid syntax

如何使“规范”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
7bsow1i6

7bsow1i61#

在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 {
  type A = Int

  override implicit val aIsFoobar: FooBar[A] = {
    lazy val aIsFoobar = ???
    implicitly[FooBar[A]]
  }
}

println(IntImpl.aIsFoobar.value) // a

NullPointerException on implicit resolution
In Scala 3, what's the canonical method for pattern match that uses an erased type?
Is there a workaround for this format parameter in Scala?
Extending an object with a trait which needs implicit member
Constructing an overridable implicitanswer
在Scala 3中,这个技巧不再起作用了。
在Scala 3中,您可以尝试创建方法inline并使用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:
  type A = Int
  override inline given aIsFoobar: FooBar[A] = summonInline[FooBar[A]]

println(IntImpl.aIsFoobar.value) // a

重写内联方法:https://docs.scala-lang.org/scala3/reference/metaprogramming/inline.html#rules-for-overriding
请注意,我们通过内联修改了方法的语义,隐式是在调用点解析的,而不是在定义点
一个二个一个一个
关于implicitlyimplicit的区别:
When doing implicit resolution with type parameters, why does val placement matter?
Why the Scala compiler can provide implicit outside of object, but cannot inside?answer
Setting abstract type based on typeclass
SYB cast function in Scala
In scala 2, can macro or any language feature be used to rewrite the abstract type reification mechanism in all subclasses? How about scala 3?
In Scala 2.13, why is it possible to summon unqualified TypeTag for abstract type?
在Scala 2中,内联可以通过Scala 2 macros实现。
Implicit Json Formatter for value classes in Scala
在https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html#abstract-implicits上
Scala 2中的抽象隐式瓦尔或def可以在Scala 3中使用常规抽象定义和给定的别名来表示。

implicit def symDecorator: SymDecorator

在Scala 3中可以表示为

def symDecorator: SymDecorator
given SymDecorator = symDecorator

您可以询问如何在Scala 3中覆盖implicit而不更改定义站点语义,可能只是手动解析implicit而不是使用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:
  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
}

更通用但不太传统的解决方案是使用Scala 3 macros +编译器内部构件

// 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]
// 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:
  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
}

Finding the second matching implicit
或者可以尝试将A作为类型参数而不是类型成员

trait FooBar[X]
object FooBar:
  given FooBar[Int] with {}

trait Intf[A: FooBar]

object IntImpl extends Intf[Int]

相关问题