Eri*_*lun 43 scala logic-programming constraint-programming type-level-computation shapeless
考虑到https://speakerdeck.com/folone/theres-a-prolog-in-your-scala,我想"滥用"Scala类型系统来查找CanBuildFrom符合给定条件的所有实例.Prolog风格,我会评估以下伪代码的行:
can_build_from(Src, int, list[int])
Src = somecollectiontype1[int]
Src = somecollectiontype2[int]
... etc
Run Code Online (Sandbox Code Playgroud)
即运行时将查找Src满足该语句的所有值can_build_from(Src, int, list[int]).
现在,我知道,原始的约束/逻辑编程环境,斯卡拉隐含查询系统,并不意味着使用这种技巧,并且不能用于"重返"不止一个发现价值Src出来的箱,所以我的问题是:是否有一个"魔术",使其工作,所以无论如何我会得到所有可能的值X的CanBuildFrom[X, Int, List[Int]]?
附加示例:
trait CanFoo[T, U]
implicit val canFooIntString = new CanFoo[Int, String] {}
implicit val canFooDblString = new CanFoo[Double, String] {}
implicit val canFooBoolString = new CanFoo[Boolean, String] {}
implicit val canFooIntSym = new CanFoo[Int, Symbol] {}
implicit val canFooDblSym = new CanFoo[Double, Symbol] {}
implicit val canFooBoolSym = new CanFoo[Boolean, Symbol] {}
Run Code Online (Sandbox Code Playgroud)
现在我想查询CanFoo[X, String]并回来X ? [Int, Double, Boolean],或者CanFoo[Int, X]回来X ? [String, Symbol].
或者,CanFoo[X, String]将返回List(canFooIntString, canFooDblString, canFooBoolString),即CanFoo该匹配的所有实例.
这可以通过编译器内部来完成(至少在某些情况下)
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}"
}
}
Run Code Online (Sandbox Code Playgroud)
allImplicits[CanFoo[_, String]]
// List(value canFooBoolString, value canFooDblString, value canFooIntString)
Run Code Online (Sandbox Code Playgroud)
在 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)
}
Run Code Online (Sandbox Code Playgroud)
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]]
//"CanFoo.canFooIntString", "CanFoo.canFooBoolString", "CanFoo.canFooDblString"
Run Code Online (Sandbox Code Playgroud)
斯卡拉 3.2.0。