在某些情况下,可以通过编译器内部的方法来实现这一点。
import scala.language.experimental.macros
import scala.reflect.internal.util
import scala.reflect.macros.{blackbox, contexts}
object Macros {
def allImplicits[A]: List[String] = macro impl[A]
def impl[A: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val context = c.asInstanceOf[contexts.Context]
val global: context.universe.type = context.universe
val analyzer: global.analyzer.type = global.analyzer
val callsiteContext = context.callsiteTyper.context
val tpA = weakTypeOf[A]
val search = new analyzer.ImplicitSearch(
tree = EmptyTree.asInstanceOf[global.Tree],
pt = tpA.asInstanceOf[global.Type],
isView = false,
context0 = callsiteContext.makeImplicit(reportAmbiguousErrors = false),
pos0 = c.enclosingPosition.asInstanceOf[util.Position]
)
q"${search.allImplicits.map(_.tree.symbol.toString).distinct}"
}
}
allImplicits[CanFoo[_, String]]
在2.13.0中进行了测试。
在2.13.10仍然有效。
Scala 3的实现方式类似。
import dotty.tools.dotc.typer.{Implicits => dottyImplicits}
import scala.quoted.{Expr, Quotes, Type, quotes}
inline def allImplicits[A]: List[String] = ${impl[A]}
def impl[A: Type](using Quotes): Expr[List[String]] = {
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[dotty.tools.dotc.core.Types.Type],
dotty.tools.dotc.ast.tpd.EmptyTree,
Position.ofMacroExpansion.asInstanceOf[dotty.tools.dotc.util.SourcePosition].span
)
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(search.wildProto)
}
else c.implicits.eligible(search.wildProto)
else search.implicitScope(search.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 implicitStrs = (contextualImplicits ++ filteredNonContextual).collect {
case success: dottyImplicits.SearchSuccess => success.tree.asInstanceOf[ImplicitSearchSuccess].tree.show
}
Expr(implicitStrs)
}
trait CanFoo[T, U]
object CanFoo {
given canFooIntString: CanFoo[Int, String] with {}
given canFooDblString: CanFoo[Double, String] with {}
given canFooBoolString: CanFoo[Boolean, String] with {}
given canFooIntSym: CanFoo[Int, Symbol] with {}
given canFooDblSym: CanFoo[Double, Symbol] with {}
given canFooBoolSym: CanFoo[Boolean, Symbol] with {}
}
allImplicits[CanFoo[_, String]]
Scala 3.2.0。
X
可以是所有T
的List[T]
... 我猜那不是你想要的。 - Miles SabinList[Int]
,也可以吗?也许我不理解CanBuildFrom,但这个想法通常可以实现吗? - Erik Kaplundef canFooDefaultUnit[T] = new CanFoo[T, Unit] {}
... 您会期望第一个类型参数的集合是什么? - Miles SabinCanFoo[X, Unit]
,你的意思是什么?我想我明白了你的观点,但这是否意味着它是不可能的,或者至少通常是不可能的? - Erik Kaplun