scala - 找到第二个匹配的隐式
问题描述
考虑以下设置:
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
带有其他隐含参数的 a(并且可能在派生 other 时发挥间接作用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
^
解决方案
Dirty 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.0materializeSecondFoo.say
中应该替换为
val m = materializeSecondFoo
m.say
推荐阅读
- nearprotocol - 用户“登录”而不指定合同 ID
- ruby-on-rails - ActiveAdmin set menu false by default
- python - 目标函数关于 cvxpy 中参数的导数
- flutter - flutter :为什么 `Matrix4.rotationY` 不沿 Y 轴旋转?
- ios - 如果在成功下载之前重用单元格,则取消 UICollectionViewCell 中的 REST 请求任务
- python - 如何在 Django 中调用模型
- dataweave - 如何获取与 requesURI 匹配的键和值对象
- json - 如何在 FutureBuilder 中解析从本地主机接收的 JSON 对象
- node.js - 与 AWS Transcribe 集成,Rekognition 与 amazon-chime-sdk-js 集成
- python - OpenCV相机标定和图像元素高度估计