【问题标题】:Is there a type-class that checks for existence of at least one implicit of a type?是否有一个类型类来检查是否存在至少一个隐式类型?
【发布时间】:2023-04-02 05:16:01
【问题描述】:

我有一个特征Foo[T, U] 和一个类型级算法,它给出了一个L <: HList 和一个目标类型U,告诉我L 中是否存在T,这样就有一个隐含的@987654326 @ 在适用范围。这是使用以下类型类实现的:

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
}

如果范围内根本没有任何TFoo[T, U],我希望根本不进行搜索,因为那时我们已经知道算法不会完成。换句话说,我想要一个类型类trait Exists[F[_]],当且仅当范围内至少有一个隐式F 时才存在其实例,因此函数Search.apply 具有签名:

def apply[L <: HList, U](implicit ev: Exists[Foo[_, U]], s: Search[L, U]): U = null

在这种情况下,如果范围内有任何隐式 Foo,编译器只会尝试解析 s

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

【问题讨论】:

    标签: scala shapeless scala-macros type-level-computation scala-compiler


    【解决方案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
    

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

    【讨论】:

    • 这行得通,但此时我不建议使用 Scala 2 宏执行此操作,更不用说像您在此处所做的那样转换为 Scala 2 编译器内部。除非编译时间完全不可接受,否则您展示的非宏 Search 是可行的方法。
    • 感谢 Dmytro,我希望这只能使用宏来完成。我也同意@MilesSabin 的观点,即这可能不会对我的Search 算法提供显着的编译时改进,但仍然认为这是一个有趣的问题......在没有宏的scala 3 中这样的事情是可能的?
    • 在 Scala 3 中可以通过专门的内联和隐式匹配来完成。在 shapeless 的 shapeless-3 分支中有一些示例或相关的东西。
    • @DmytroMitin 是的,这会导致歧义。我的意思是 Search 类型类可以通过专门的内联和隐式匹配来实现。
    • 也许可以利用 Scala 3 区分模糊隐含和找不到隐含这一事实,尽管它也提供了自己的类型类。
    【解决方案2】:

    在 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

    您的 Exists 类型类现在看起来像这样(给定的语法可能很快会改变,但您明白了):

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

    查看Scastie

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多