Существует ли класс типов, который проверяет наличие хотя бы одного неявного типа?
У меня есть черта Foo[T, U]
и алгоритм уровня типа, который дал L <: HList
и тип цели U
, говорит мне, существует ли T
в L
такой, что есть неявное Foo[T, U]
в области. Это реализовано с использованием следующего класса типов:
trait Search[L <: HList, U]
object Search {
def apply[L <: HList, U](implicit s: Search[L, U]): U = null
...
}
и у нас есть следующее:
object Test {
type L = Int :: String :: HNil
implicit val foo: Foo[String, Boolean] = null
Search[L, Boolean] //compiles
Search[L, Double] //does not compile
}
Я хотел бы, чтобы поиск вообще не проводился, если нет Foo[T, U]
для любого T
вообще по объему, так как тогда мы уже знаем, что алгоритм не будет завершен. Другими словами, я хочу тип-класс trait Exists[F[_]]
для которых существуют случаи, если и только если есть хотя бы один неявный F
по объему, поэтому функция Search.apply
вместо этого есть подпись:
def apply[L <: HList, U](implicit ev: Exists[Foo[_, U]], s: Search[L, U]): U = null
В этом случае компилятор будет пытаться разрешить только s
если есть какие-либо неявные Foo
в области.
Можно ли определить такой класс типов? Уже существует?
1 ответ
Пытаться
import scala.language.experimental.macros
import scala.reflect.macros.{blackbox, contexts}
trait Exists[A]
object Exists {
implicit def materialize[A]: Exists[A] = 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 searchResult = analyzer.inferImplicit(
tree = EmptyTree.asInstanceOf[global.Tree],
pt = tpA.asInstanceOf[global.Type],
reportAmbiguous = false,
isView = false,
context = callsiteContext,
saveAmbiguousDivergent = true,
pos = c.enclosingPosition.asInstanceOf[global.Position]
)
val isAmbiguous = callsiteContext.reporter.firstError match {
case Some(analyzer.AmbiguousImplicitTypeError(_,_)) => true
case _ => false
}
if (searchResult.isSuccess || searchResult.isAmbiguousFailure || isAmbiguous)
q"new Exists[$tpA] {}"
else c.abort(c.enclosingPosition, s"no implicit $tpA")
}
}
Контрольная работа
// no implicit Int
// implicitly[Exists[Int]] // doesn't compile
implicit val i: Int = 1
implicitly[Exists[Int]] // compiles
implicit val i: Int = 1
implicit val i1: Int = 2
implicitly[Exists[Int]] // compiles
I guess original Search
был
trait Foo[U, V]
trait Search[L <: HList, V]
trait LowPrioritySearch {
implicit def tail[H, T <: HList, V](implicit search: Search[T, V]): Search[H :: T, V] = null
}
object Search extends LowPrioritySearch {
def apply[L <: HList, U](implicit s: Search[L, U]): U = null.asInstanceOf[U]
implicit def head[U, T <: HList, V](implicit foo: Foo[U, V]): Search[U :: T, V] = null
}
Теперь с Exists
def apply[L <: HList, U](implicit ev: Exists[Foo[_, U]], s: Search[L, U]): U = null.asInstanceOf[U]
works as well
Search[L, Boolean] //compiles
// Search[L, Double] //does not compile
Tested in 2.13.0
libraryDependencies ++= Seq(
scalaOrganization.value % "scala-reflect" % scalaVersion.value,
scalaOrganization.value % "scala-compiler" % scalaVersion.value
)
В Scala 3
scala.util.Not
(скоро будет
NotGiven
?), существующий, если имплициты данного типа не найдены, можно использовать для этого:
implicit val b: Byte = 1
summon[Not[Not[Byte]]] //compiles
implicit val i: Int = 0
implicit val i2: Int = 2
summon[Not[Not[Int]]] //compiles
summon[Not[Not[String]]] //doesn't compile - Not[String] not found
Посмотри на Скэсти.
Твой
Exists
typeclass теперь может выглядеть так (синтаксис для givens скоро может измениться, но вы поняли идею):
@annotation.implicitNotFound("No implicit of type ${T} was found")
trait Exists[T]
object Exists {
given [T](using Not[Not[T]]) as Exists[T]
}
Посмотри на Скэсти.