I am trying to find a way, in shapeless, to prove that a given product type extends no sealed trait, and hence does not belong to any coproduct. Given the following sealed trait hierarchy:
sealed trait Foo
case class Bar(a: Char) extends Foo
case class Baz(b: Int) extends Foo
I know I can use shapeless.ops.coproduct.Basis
to prove that a given choice, or subsequence of choices, belongs to a co-product. Eg:
import shapeless._
type L = Bar :+: Baz :+: CNil
implicitly[ops.coproduct.Basis[L, Bar :+: CNil]]
What I am after now, is an operation to get the coproduct from a choice. Eg. given Bar
or Baz
, I would like to get back L
, or alternatively, the type of the sealed base trait Foo
.
Is this something that shapeless can do? alternatively, is it possible to do it with macros?
Update:
I ended up writing a fairly involved implicit macro...
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait SealedBaseTraitOf[A] {
type Repr
}
object SealedBaseTraitOf {
def materializeImpl[A](c: whitebox.Context)(implicit tag: c.WeakTypeTag[A]): c.Expr[SealedBaseTraitOf[A]] = {
import c.universe._
val a = weakTypeOf[A].typeSymbol
val baseClasses = a.asClass.baseClasses
val maybeBaseTrait =
baseClasses
.find(t => t.asClass.isTrait && t.asClass.isSealed)
.map(_.asType.name)
val repr = maybeBaseTrait.map(t => tq"$t").getOrElse(tq"Nothing")
println(s"Got a repr: $repr")
c.Expr[SealedBaseTraitOf[A]](q"""
new SealedBaseTraitOf[$a] {
type Repr = $repr
}
""")
}
implicit def materialize[A]:SealedBaseTraitOf[A] = macro materializeImpl[A]
}
Putting all together, the macro can be used as follows:
case class ExtendsNothing(a: Int)
sealed trait X
case class ExtendsX(b: Char) extends X
import shapeless._
val bt1 = the[SealedBaseTraitOf[ExtendsNothing]]
implicitly[bt1.Repr =:= Nothing]
val bt2 = the[SealedBaseTraitOf[ExtendsX]]
implicitly[bt2.Repr =:= X]
val coprodX = Generic[X]
val coprodBt2 = Generic[bt2.Repr]
implicitly[coprodX.Repr =:= coprodBt2.Repr]
implicitly[ops.coproduct.Basis[coprodBt2.Repr, ExtendsX :+: CNil]]
While getting close to a solution, I am still hoping to find something a bit less involved, possibly which doesn't involve using macros.