I'm trying to generate LabelledGeneric for Coproduct, so that it can be used instead of typical sealed trait hierarchy. So far I was able to do it by explicit specification of labels for DefaultSymbolicLabelling, but I feel it should be possible to derive it automatically from coproduct's type members.
/**
* 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))
}
See code below with sealed trait; in general I'd like to achieve similar behavior, just without sealing the trait.
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))
}
Any hints? Looked through shapeless macros, but so far I found nothing useful...
m.
For a not
sealedtrait, the instances ofGeneric/LabelledGenericdefined in Shapeless can't work.All such macros are using
.knownDirectSubclasses. It works only for a sealed trait.Scala reflection: knownDirectSubclasses only works for sealed traits?
For a not sealed trait I can always add inheritors of
Basein a different file (case class Case4() extends Base) or even at runtime (toolbox.define(q"case class Case4() extends Base")).If you're interested only in inheritors defined in the current file then maybe you can avoid usage of
.knownDirectSubclassesand write a macro traversing the AST of current file and looking for the inheritors.It's not hard
So you can derive
DefaultSymbolicLabellingas followsHere is the code with traversing. I'm introducing type class
KnownSubclasses(This implementation doesn't work for generic trait and classes.)
So you can derive
GenericandDefaultSymbolicLabelling(and thereforeLabelledGeneric) as follows