问题描述
我遇到一种情况,我试图在单例类型上使用隐式分辨率。如果我在编译时知道单例类型,则可以很好地工作:
object Main {
type SS = String with Singleton
trait Entry[S <: SS] {
type out
val value: out
}
implicit val e1 = new Entry["S"] {
type out = Int
val value = 3
}
implicit val e2 = new Entry["T"] {
type out = String
val value = "ABC"
}
def resolve[X <: SS](s: X)(implicit x: Entry[X]): x.value.type = {
x.value
}
def main(args: Array[String]): Unit = {
resolve("S") //implicit found! No problem
}
}
但是,如果我在编译时不知道这种类型,那么我就会遇到问题。
def main(args: Array[String]): Unit = {
val string = StdIn.readLine()
resolve(string) //Can't find implicit because it doesn't kNow the singleton type at runtime.
}
反正我可以解决这个问题吗?也许某些方法需要使用String
并返回该字符串的单例类型?
def getSingletonType[T <: SS](string: String): T = ???
那我也许可以做
def main(args: Array[String]): Unit = {
val string = StdIn.readLine()
resolve(getSingletonType(string))
}
还是这不可能?也许只有在编译时就了解所有信息,您才能做这种事情?
解决方法
如果您知道Entry
在编译时的所有可能实现-只有在密封的情况下才可能实现-那么您可以使用宏来创建映射/部分函数String -> Entry[_]
。 / p>
由于这是可以扩展的,所以恐怕最好在运行时反射中必须扫描整个类路径以找到所有可能的实现。
但是即使那样,您仍然必须以某种方式将这个String
嵌入到每个实现中,因为JVM字节码对单例类型和实现之间的映射一无所知-只有Scala编译器知道。然后使用它来查找所有实现中是否有一个(并且恰好是一个)与您的值匹配-如果是隐式,如果在同一范围内一次存在两个,则编译会失败,但是您可以有多个只要不要将它们一起出现在同一范围内即可。运行时反射将是全局的,因此将无法避免冲突。
因此,没有很好的解决方案来使此编译时调度动态化。您可以通过例如自己编写Map[String,Entry[_]]
并使用get
函数来处理丢失的图片。
通常隐式在编译时解析。但是val string = StdIn.readLine()
仅在运行时才知道。原则上,您可以将隐式解析推迟到运行时,但是您只能在运行时而不是在编译时(静态类型等)应用此类解析的结果
object Entry {
implicit val e1 = ...
implicit val e2 = ...
}
import scala.reflect.runtime.universe._
import scala.reflect.runtime
import scala.tools.reflect.ToolBox
val toolbox = ToolBox(runtime.currentMirror).mkToolBox()
def resolve(s: String): Any = {
val typ = appliedType(
typeOf[Entry[_]].typeConstructor,internal.constantType(Constant(s))
)
val instanceTree = toolbox.inferImplicitValue(typ,silent = false)
val instance = toolbox.eval(toolbox.untypecheck(instanceTree)).asInstanceOf[Entry[_]]
instance.value
}
resolve("S") // 3
val string = StdIn.readLine()
resolve(string)
// 3 if you enter S
// ABC if you enter T
// "scala.tools.reflect.ToolBoxError: implicit search has failed" otherwise
请注意,我将隐式对象放入类型类的伴随对象中,以使其在隐式范围内(因此在工具箱范围内)可用。否则,代码应稍作修改:
object EntryImplicits {
implicit val e1 = ...
implicit val e2 = ...
}
// val instanceTree = toolbox.inferImplicitValue(typ,silent = false)
// should be replaced with
val instanceTree =
q"""
import path.to.EntryImplicits._
implicitly[$typ]
"""
在您的代码中,import path.to.EntryImplicits._
是import Main._
。