0

我有一种情况,我试图在单例类型上使用隐式解析。如果我在编译时知道单例类型,这将非常有效:

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.
}

无论如何我可以解决这个问题吗?也许某些方法采用 aString并返回该字符串的单例类型?

def getSingletonType[T <: SS](string: String): T = ???

那么也许我可以做

def main(args: Array[String]): Unit = {
    val string = StdIn.readLine()
    resolve(getSingletonType(string))
}

或者这是不可能的?如果您在编译时知道所有信息,也许您只能做这种事情?

4

2 回答 2

3

如果您知道Entry编译时所有可能的实现(只有在密封时才有可能),那么您可以使用宏来创建映射/部分函数String -> Entry[_]

由于这对扩展是开放的,我担心充其量一些运行时反射将不得不扫描整个类路径以找到所有可能的实现。

但即便如此,您也必须以String某种方式将这个文字嵌入到每个实现中,因为 JVM 字节码对单例类型和实现之间的映射一无所知——只有 Scala 编译器知道。然后使用它来查找在所有实现中是否有一个(并且恰好一个)与您的值匹配 - 如果在同一范围内同时有两个实现,则在隐含的情况下编译将失败,但您可以拥有多个只要不一起出现在同一范围内,就可以实现。运行时反射将是全局的,因此无法避免冲突。

所以不,没有好的解决方案可以使这个编译时调度动态化。您可以自己创建这样的调度,例如自己编写Map[String, Entry[_]]并使用get函数来处理丢失的图片。

于 2020-10-11T21:36:54.977 回答
1

通常隐式在编译时解决。但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._.

从动态生成的案例类加载数据集

于 2020-10-11T21:36:14.597 回答