Search code examples
scalashapeless

Unwrap/strip Shapeless Coproduct objects


I have a domain that looks like this:

package object tryme {
  type ALL = AlarmMessage :+: PassMessage :+: HeyMessage :+: CNil
}
import com.tryme._

trait PayloadKind
trait Command extends PayloadKind
trait Event extends PayloadKind

trait Message[+T <: PayloadKind] {
  val id: String
  val payload: T
}

case class Alarm(severity: Int) extends Event
case class Pass(reason: String) extends Event
case class Hey(order: String) extends Command

case class AlarmMessage(id: String, payload: Alarm) extends Message[Event]
case class PassMessage(id: String, payload: Pass) extends Message[Event]
case class HeyMessage(id: String, payload: Hey) extends Message[Command]

case class AvroMessage(
    id:    String,
    cargo: ALL
)

If I create an AvroMessage with cargo = HeyMessage, I'm able to serialize/de-serialse this successfully.

When I inspect the cargo field of the deserialized AvroMessage object it looks like this:

Inr(Inr(Inl(HeyMessage(art333,Hey(wow)))))

In Shapeless, how can I unwrap/strip all the Inr/Inls? Ideally I don't want to care (post de-serialization) that the payload is a HeyMessage (or any other type) if I can avoid it. I'm going to cast the cargo to Message[PayloadKind], but first I need to unwrap it.

(I saw the answer to a similar question here, but I confess I don't understand how it works, and wasn't able to adapt it.)


Solution

  • You can use shapeless.ops.coproduct.Selector

    val coproduct = Inr(Inr(Inl(HeyMessage(art333, Hey(wow)))))
    Selector[ALL, HeyMessage].apply(coproduct).get
    

    Result is HeyMessage(art333,Hey(wow)).

    https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/ops/coproduct.scala#L41-L60