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.