Использование "Пролога в Scala" для поиска доступных экземпляров классов типов
Учитывая https://speakerdeck.com/folone/theres-a-prolog-in-your-scala, я хотел бы "злоупотребить" системой типов Scala, чтобы найти все экземпляры, например: CanBuildFrom
которые соответствуют заданным критериям. В стиле Пролог я бы оценил что-то в строках следующего псевдокода:
can_build_from(Src, int, list[int])
Src = somecollectiontype1[int]
Src = somecollectiontype2[int]
... etc
т.е. среда выполнения будет искать все значения для Src
которые удовлетворяют утверждению can_build_from(Src, int, list[int])
,
Теперь я знаю, что примитивная среда программирования с ограничениями / логикой, которой является система неявного поиска Scala, не предназначена для таких трюков и не способна "вернуть" более одного найденного значения для 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] {}
Теперь я хотел бы запросить CanFoo[X, String]
и вернуться X ∈ [Int, Double, Boolean]
, или же CanFoo[Int, X]
и вернуться X ∈ [String, Symbol]
,
С другой стороны, CanFoo[X, String]
вернется List(canFooIntString, canFooDblString, canFooBoolString)
т.е. все случаи CanFoo
этот матч.
0 ответов
Это можно сделать (по крайней мере, в некоторых случаях) с помощью внутренних компонентов компилятора.
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]]
// List(value canFooBoolString, value canFooDblString, value canFooIntString)
Проверено в 2.13.0.