4

我有一个特征Foo[T, U]和一个类型级别的算法,它给定一个L <: HList和一个目标类型U,告诉我是否存在这样一个隐含T的范围。这是使用以下类型类实现的:LFoo[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]for any ,则根本不会进行搜索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

这样的类型类可以定义吗?是否已经存在?

4

2 回答 2

2

尝试

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

我猜原来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]

也可以

Search[L, Boolean] //compiles
// Search[L, Double] //does not compile

在 2.13.0 中测试

libraryDependencies ++= Seq(
  scalaOrganization.value % "scala-reflect" % scalaVersion.value,
  scalaOrganization.value % "scala-compiler" % scalaVersion.value
)
于 2019-08-14T05:18:57.070 回答
1

在 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

在Scastie中看到它。

你的Existstypeclass 现在看起来像这样(givens 的语法可能很快会改变,但你明白了):

@annotation.implicitNotFound("No implicit of type ${T} was found")
trait Exists[T]
object Exists {
  given [T](using Not[Not[T]]) as Exists[T]
}

在Scastie中看到它。

于 2020-11-30T16:30:14.997 回答