【问题标题】:Convert the fully qualifed name of a type to the type将类型的完全限定名称转换为类型
【发布时间】:2015-04-15 14:33:32
【问题描述】:

我有一个密封的特征/抽象类层次结构,需要将其子类型转换为字符串,然后将这些字符串转换回类型。此代码示例描述了我想要实现的目标:

import shapeless._

object Test extends App {

  sealed abstract class C(val i: Int)
  case object O1 extends C(1)
  case object O2 extends C(2)
  // lots of other implementations

  trait TC[A] {
    def f: String
  }
  implicit object TC1 extends TC[O1.type] {
    def f = "O1"
  }
  implicit object TC2 extends TC[O2.type] {
    def f = "O2"
  }
  object fqn {
    val o1 = implicitly[TC[O1.type]].f
    val o2 = implicitly[TC[O2.type]].f

    def asString(c: C): String = c match {
      case O1 ⇒ o1
      case O2 ⇒ o2
    }

    def fromString(s: String): C = s match {
      case `o1` ⇒ O1
      case `o2` ⇒ O2
    }
  }

  object asString extends Poly1 {
    private implicit def impl[A <: C : TC] = at[A](a ⇒ implicitly[TC[A]].f)
    def apply(c: C): String = Generic[C].to(c).map(this).unify
  }
  object fromString {
    def apply(s: String): C = ???
  }

  // This works as expected
  println(O1 eq fqn.fromString(fqn.asString(O1)))
  println(O2 eq fqn.fromString(fqn.asString(O2)))

  // Does not yet work
  println(O1 eq fromString(asString(O1)))
  println(O2 eq fromString(asString(O2)))
}

目前,fqn 中的功能一切正常,但对于C 的更多子类型,它很麻烦且难以维护。使用 shapeless 我设法摆脱了 asString 部分,但我一直在为 fromString 部分寻找解决方案。

谁能想到在 shapeless(或其他库)中实现fromString 部分的方法?

关于我的示例的更多详细信息:

  • TC 由图书馆提供,我无法更改。类型类是 由该库中的宏生成,因此 TCN 类型类 不直接存在。
  • 我不能轻易提供一个宏来生成fqn.asStringfqn.fromString 的实现,因此我正在寻找一个已经支持这种行为的库。

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    不幸的是,我不知道如何在编译时使用宏\无形魔法解决您的问题。但在运行时,解决方案可能是:

    import reflect.runtime.{currentMirror => cm}
    import scala.reflect.runtime.universe._
    
    def fromString(s: String): C = {
      val caseObjects = weakTypeOf[C].baseClasses.
         flatMap(s => s.asClass.knownDirectSubclasses)
      val map = caseObjects.map(obj => (obj.name.toString, obj)).toMap
      cm.reflectModule(map(s).companionSymbol.asModule).instance match {
        case c: C => c
      }
    }
    

    也许您必须添加isModule 条件才能仅获得case objects

    【讨论】:

    • 反射的问题是它很慢而且不是线程安全的。我可以想出一个相当冗长的解决方案,看看我自己的答案。
    【解决方案2】:

    我想出了一个解决方案:

    import shapeless._
    
    object GetAllSingletons {
      sealed trait AllSingletons[A, C <: Coproduct] {
        def values: List[A]
      }
    
      implicit def cnilSingletons[A]: AllSingletons[A, CNil] =
        new AllSingletons[A, CNil] {
          def values = Nil
        }
    
      implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit
        witness: Witness.Aux[H],
        tsc: AllSingletons[A, T]
      ): AllSingletons[A, H :+: T] =
        new AllSingletons[A, H :+: T] {
          def values = witness.value :: tsc.values
        }
    
      final class GetAllSingletones[A] {
        def apply[C <: Coproduct]()(implicit
          gen: Generic.Aux[A, C],
          singletons: AllSingletons[A, C]
        ): List[A] =
          singletons.values
      }
    
      def singletons[A] = new GetAllSingletones[A]
    }
    
    object Test extends App {
    
      sealed abstract class C(val i: Int)
      case object O1 extends C(1)
      case object O2 extends C(2)
      // lots of other implementations
    
      trait TC[A] {
        def f: String
      }
      implicit object TC1 extends TC[O1.type] {
        def f = "O1"
      }
      implicit object TC2 extends TC[O2.type] {
        def f = "O2"
      }
    
      object asString extends Poly1 {
        private implicit def impl[A <: C : TC] = at[A](a ⇒ implicitly[TC[A]].f)
        def apply(c: C): String = Generic[C].to(c).map(this).unify
      }
      object fromString {
        val singletons: Map[String, C] = GetAllSingletons.singletons[C]().map(s ⇒ asString(s) → s)(collection.breakOut)
        def apply(s: String): C = singletons(s)
      }
    
      println(O1 eq fromString(asString(O1)))
      println(O2 eq fromString(asString(O2)))
    }
    

    GetAllSingletons 中的定义很繁琐,但我找不到进一步简化它的方法。这个想法来自this questionAnother question 的答案提供了一个更简单的解决方案,但它只能以更有限的方式工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 2019-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      相关资源
      最近更新 更多