I'm trying to come up with a parser framework for ADT hierarchy. I want it to automatically derive a parser defined at either "leaf" (case class) level or "node" (intermediate sealed trait) level. Usage would look like:
sealed trait Chrum
case object Go extends Chrum
sealed trait Bigh extends Chrum
case class Do(doo: Double) extends Bigh
case class Bo(bo: Int) extends Bigh
implicit val goParser:Parser[Go] = ???
implicit val bighParser:Parser[Bigh] = ???
import MyParsers._
implicitly[Parser[Chrum]].parse(myinput) // implicicts in MyParsers would derive parser for Chrum from the above 2
I followed the "simple" approach from circe-derivation examples. It failed to find the bighParser
, but worked if I defined separate doParser
and boParser
Root cause seems to be this:
Generic[Chrum]
//returns: shapeless.Generic[Chrum]{type Repr = Go.type :+: Do :+: Bo :+: shapeless.CNil}
// Bigh is erased, no way to derive it's parser :(
I think what I need here is autoderivation of sth like Go.type :+: Bigh :+: CNil
for the above hierarchy. Is this possible with shapeless?
One can never be 100% sure that something is not possible with shapeless (so I'm looking forward to answers proving me wrong), but: I think what you intend to do is not possible.
Shapeless represents sealed trait hierarchies as Coproduct
in a way that it will ignore any abstract members of the ADT:
scala> sealed trait Chrum
| case object Go extends Chrum
| sealed trait Bigh extends Chrum
| case class Do(doo: Double) extends Bigh
| case class Bo(bo: Int) extends Bigh
trait Chrum
object Go
trait Bigh
class Do
class Bo
scala> shapeless.Generic[Chrum]
val res2: shapeless.Generic[Chrum]{type Repr = Bo :+: Do :+: Go.type :+: shapeless.CNil} = shapeless.Generic$$anon$1@38d308e7
Trying to force shapeless to provide your desired representation fails because the underlying macro implementation does not support that:
scala> shapeless.Generic.materialize[Chrum, Bigh :+: Go.type :+: shapeless.CNil]
^
error: type mismatch;
found : shapeless.Generic[Chrum]{type Repr = Bo :+: Do :+: Go.type :+: shapeless.CNil}
required: shapeless.Generic.Aux[Chrum,Bigh :+: Go.type :+: shapeless.CNil]
(which expands to) shapeless.Generic[Chrum]{type Repr = Bigh :+: Go.type :+: shapeless.CNil}
N.B.: what you intend to do would be possible with the derivation mechanisms of Scala 3 because there, sealed traits with nested sealed traits are represented differently:
scala> summon[scala.deriving.Mirror.Of[Chrum]]
val res0:
scala.deriving.Mirror.Sum{
type MirroredMonoType = Chrum; type MirroredType = Chrum;
type MirroredLabel = "Chrum"; type MirroredElemTypes = (Go.type, Bigh);
type MirroredElemLabels = ("Go", "Bigh")
} = anon$1@36db5318