我正在尝试为Coproduct生成LabelledGeneric,这样它就可以用来代替典型的sealed trait
层次结构。到目前为止,我能够通过显式指定DefaultSymbolicLabelling
的标签来实现这一点,但我觉得应该可以从coproduct的类型成员自动派生它。
/**
* So far I found no way to derive `L` and `l` from `C`.
*/
object ShapelessLabelledGenericForCoproduct extends App {
trait Base // not sealed!
case class Case1(a: Int) extends Base
case class Case2(a: String) extends Base
case class Case3(b: Boolean) extends Base
object Base {
type C = Case1 :+: Case2 :+: Case3 :+: CNil
type L = (Symbol @@ "Case1") :: (Symbol @@ "Case2") :: (Symbol @@ "Case3") :: shapeless.HNil
val l: L = tag["Case1"](Symbol("Case1")) :: tag["Case2"](Symbol("Case2")) :: tag["Case3"](Symbol("Case3")) :: HNil
implicit def myGeneric: Generic.Aux[Base, C] = Generic.instance[Base, C](
v => Coproduct.runtimeInject[C](v).get,
v => Coproduct.unsafeGet(v).asInstanceOf[Base]
)
implicit def mySymbolicLabelling: DefaultSymbolicLabelling.Aux[Base, L] = DefaultSymbolicLabelling.instance[Base, L](l)
}
val lgen = LabelledGeneric[Base]
val repr = lgen.to(Case1(123))
println(lgen.from(repr))
}
见下面的代码与密封的特点;一般来说,我希望实现类似的行为,只是不密封该特性。
object ShapelessLabelledGenericForSealedTrait extends App {
sealed trait Base
case class Case1(a: Int) extends Base
case class Case2(a: String) extends Base
case class Case3(b: Boolean) extends Base
val lgen = LabelledGeneric[Base]
val repr = lgen.to(Case1(123))
println(lgen.from(repr))
}
有什么线索吗?看了看无形的宏,但到目前为止我没发现什么有用的...
米。
1条答案
按热度按时间ecfsfe2w1#
对于一个非
sealed
的trait,在Shapeless中定义的Generic
/LabelledGeneric
的示例不能工作。所有这样的宏都使用
.knownDirectSubclasses
,它只对密封的trait有效。Scala reflection: knownDirectSubclasses only works for sealed traits?
对于一个未密封的trait,我总是可以在不同的文件(
case class Case4() extends Base
)中添加Base
的继承者,甚至在运行时(toolbox.define(q"case class Case4() extends Base")
)。如果您只对当前文件中定义的继承者感兴趣,那么您可以避免使用
.knownDirectSubclasses
,并编写一个宏来遍历当前文件的AST并查找继承者。到目前为止,我还没有找到从
C
派生L
和l
的方法。这并不难
因此,可以按如下方式推导
DefaultSymbolicLabelling
下面是遍历的代码,我将引入类型类
KnownSubclasses
一个三个三个一个
(This实现不适用于泛型特征和类。)
因此,可以按如下所示推导
Generic
和DefaultSymbolicLabelling
(从而推导出LabelledGeneric