我有一个密封的特征/抽象类层次结构,需要将其子类型转换为字符串,并将这些字符串转换回类型。此代码示例描述了我想要实现的目标:
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
的更多子类型来说,这很麻烦且难以维护。由于没有形状,我设法逃脱了asString
部分,但我仍然在寻找fromString
部分的解决方案。
任何人都可以想到在无形(或其他库)中实现fromString
部分的方法吗?
关于我的例子的更多细节:
TC
由图书馆提供,我无法对其进行更改。类型类是
由此库中的宏生成,因此是TCN类型类
不要直接存在。fqn.asString
和fqn.fromString
实施的宏,因此我正在寻找已支持此行为的库。答案 0 :(得分:0)
不幸的是,我不知道如何使用宏\ shapeless magic在编译时解决你的问题。但在运行时,解决方案可能是:
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
。
答案 1 :(得分:0)
我设法找到了解决方案:
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 question。 Another question的答案提供了一个更简单的解决方案,但它只能以更有限的方式运行。