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
sealed
trait, the instances ofGeneric
/LabelledGeneric
defined 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
Base
in 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
.knownDirectSubclasses
and write a macro traversing the AST of current file and looking for the inheritors.It's not hard
So you can derive
DefaultSymbolicLabelling
as 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
Generic
andDefaultSymbolicLabelling
(and thereforeLabelledGeneric
) as follows