Get Coproduct from choice

Viewed 165

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.

0 Answers
Related