有没有办法隐式获取对象的实例

时间:2018-02-22 13:32:47

标签: scala shapeless type-level-computation frameless

我们希望为frameless创建一个任意Enumerations的编码器 基本上是创建从任意EnumerationByte的双向映射。目前,我们不太理想的解决方案是在所有Enumeration实例上提供证据,以便解串器可以在该实例上调用apply,这是一种从Enumeration创建Byte的方法}。我们希望找到一种不定义这些隐式值的方法,而是自动从E类型中选择它们。据我们所知,对象类型与单个实例一一对应,因此我们希望有一种机制可以做到这一点。

例如,以下作品

import frameless._

object Color extends Enumeration {
  type Color = Value
  val Red, Green, Blue = Value
}
object State extends Enumeration {
  type State = Value
  val Running, Stopped, Finished = Value
}

implicit val colorEvidence = Color // we want to spare these lines
implicit val stateEvidence = State // we want to spare these lines

implicit def enumToByteInjection[E <: Enumeration](implicit e: E): 
  Injection[E#Value, Byte] = Injection(_.id.toByte, e.apply(_))

2 个答案:

答案 0 :(得分:1)

解决方案1(反思)

这里编译并在使用scalac 2.12.4编译时运行:

object Color extends Enumeration {
  type Color = Value
  val Red, Green, Blue = Value
}
object State extends Enumeration {
  type State = Value
  val Running, Stopped, Finished = Value
}

/** Dummy replacement with similar signature */
class Injection[A, B]()


import scala.reflect.runtime.universe.TypeTag

object ItDoesNotWorkInReplObjectsMustBeTopLevel {

  implicit def enumToByteInjection[E <: Enumeration](implicit tt: TypeTag[E]): Injection[E#Value, Byte] = {
    val ru = scala.reflect.runtime.universe
    val classLoaderMirror = ru.runtimeMirror(getClass.getClassLoader)
    val moduleSymbol = ru.typeOf[E].termSymbol.asModule
    val moduleMirror = classLoaderMirror.reflectModule(moduleSymbol)
    val companionObject = moduleMirror.instance.asInstanceOf[E]

    println(s"/* 1 */ Materialize companion object $companionObject out of nothing!")
              /* 2 */ ???
              /* 3 */ // profit!
  }

  /** A function that requires implicit `Injection` */
  def testNeedsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = 
    println("replace ??? above to continue here")


  def main(args: Array[String]): Unit = {
    /** Test whether an implicit injection is constructed */
    testNeedsInjection[Color.type] // compiles (crashes, as expected, but compiles)

  }
}

由于???上的实现缺失,它当然会崩溃,但是隐藏的伴随对象被召唤后会出现

陷阱:

  • 以脚本
  • 运行时不起作用
  • 不在REPL中工作(需要对象是顶级的)
  • 确保使用正确的ClassLoader,提供类似classOf[Unit].classLoader的内容,以NoSuchClassException s。
  • 需要TypeTag s作为枚举(当与顶层的具体枚举一起使用时不应该成为问题,但如果该方法应该成为问题可能会成为一个问题被埋在图书馆深处,但仍然可以进入表面&#34;然后你必须通过每个方法将TypeTag拉到表面。)

解决方案2(隐含对象)

如果您控制了所有枚举,那么您可以简单地声明枚举对象implicit。以下编译很好,所有implicits按预期插入:

implicit object Color extends Enumeration {
  type Color = Value
  val Red, Green, Blue = Value
}
implicit object State extends Enumeration {
  type State = Value
  val Running, Stopped, Finished = Value
}

/** Dummy replacement with similar signature */
class Injection[A, B]()

implicit def enumToByteInjection[E <: Enumeration](implicit e: E): Injection[E#Value, Byte] = ???

/** A function that requires implicit `Injection` */
def needsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = ???

/** Test whether an implicit injection is constructed */
needsInjection[Color.type] // compiles (crashes, as expected, but compiles)

答案 1 :(得分:1)

解决方案3(无形魔法)

我们也可以使用Shapeless Witness es来为我们的枚举类型召唤单例值。 Shapeless使用编译时反射和代码生成来为给定类型创建实例。

import shapeless._

implicit def enumToByteInjection[E <: Enumeration](implicit w: Witness.Aux[E]):
  Injection[E#Value, Byte] = Injection(_.id.toByte, w.value.apply(_))