代码之家  ›  专栏  ›  技术社区  ›  Alec

寻找第二个匹配隐式

  •  1
  • Alec  · 技术社区  · 5 年前

    考虑以下设置:

    trait Foo[A]
    object Foo extends Priority2
    
    trait Priority0 {
       implicit def foo1: Foo[Int] = new Foo[Int] {}
    }
    trait Priority1 extends Priority0 {
       implicit def foo2: Foo[Boolean] = new Foo[Boolean] {}
    }
    trait Priority2 extends Priority1 {
       implicit def foo3: Foo[Double] = new Foo[Double] {}
    }
    

    现在,在REPL中(已加载上述代码),我可以执行以下操作:

    scala> def implicitlyFoo[A](implicit foo: Foo[A]) = foo
    implicitlyFoo: [A](implicit foo: Foo[A])Foo[A]
    
    scala> implicitlyFoo
    res1: Foo[Double] = Priority2$$anon$3@79703b86
    

    有没有一种方法可以使用某种类型级别的魔法进行编码,我想跳过这些实例 A =:= Double ,但还是让类型推断找出什么 A

    我不想留下阴影 foo3 这是一个MVCE:在我的真实案例中, foo3 是一个 def 与其他隐式论点(并可能在推导其他 Foo 是的)。

    我试过了 =:!= 从变形到徒劳:

    scala> import shapeless._
    import shapeless._
    
    scala> def implicitlyFoo2[A](implicit foo: Foo[A], ev: A =:!= Double) = foo
    implicitlyFoo2: [A](implicit foo: Foo[A], implicit ev: A =:!= Double)Foo[A]
    
    scala> implicitlyFoo2
    <console>:16: error: ambiguous implicit values:
     both method neqAmbig1 in package shapeless of type [A]=> A =:!= A
     and method neqAmbig2 in package shapeless of type [A]=> A =:!= A
     match expected type Double =:!= Double
           implicitlyFoo2
           ^
    
    0 回复  |  直到 5 年前
        1
  •  0
  •   Dmytro Mitin    5 年前

    Dirry hack是将宏上下文向下转换到其实现中,并使用编译器内部。

      import scala.language.experimental.macros
      import scala.reflect.macros.whitebox
    
      trait Foo[A] {
        def say: String
      }
    
      trait Priority0 {
        implicit def foo1: Foo[Int] = new Foo[Int] {
          override def say: String = "int"
        }
      }
    
      trait Priority1 extends Priority0 {
        implicit def foo2: Foo[Boolean] = new Foo[Boolean] {
          override def say: String = "bool"
        }
      }
    
      trait Priority2 extends Priority1 {
        implicit def foo3: Foo[Double] = new Foo[Double] {
          override def say: String = "double"
        }
      }
    
      object Foo extends Priority2
    
    
      def materializeSecondFoo[A]: Foo[A] = macro impl
    
      def impl(c: whitebox.Context): c.Tree = {
        import c.universe._
    
        val context = c.asInstanceOf[reflect.macros.runtime.Context]
        val global: context.universe.type = context.universe
        val analyzer: global.analyzer.type = global.analyzer
    
        var infos = List[analyzer.ImplicitInfo]()
    
        new analyzer.ImplicitSearch(
          tree = EmptyTree.asInstanceOf[global.Tree],
          pt = typeOf[Foo[_]].asInstanceOf[global.Type],
          isView = false,
          context0 = global.typer.context.makeImplicit(reportAmbiguousErrors = false),
          pos0 = c.enclosingPosition.asInstanceOf[global.Position]
        ) {
          override def searchImplicit(
                                       implicitInfoss: List[List[analyzer.ImplicitInfo]],
                                       isLocalToCallsite: Boolean
                                     ): analyzer.SearchResult = {
            val implicitInfos = implicitInfoss.flatten
            if (implicitInfos.nonEmpty) {
              infos = implicitInfos
            }
            super.searchImplicit(implicitInfoss, isLocalToCallsite)
          }
        }.bestImplicit
    
        val secondBest = infos.tail.head
    
        global.gen.mkAttributedRef(secondBest.pre, secondBest.sym).asInstanceOf[Tree]
      }
    
      materializeSecondFoo.say // bool
    

    在2.12.8中测试。出于 shapeless.Cached .


    在2.13.0中 materializeSecondFoo.say 应该被替换为

    val m = materializeSecondFoo
    m.say