scalacovarianceavro4s

Avro4s in Scala 3, create decoder for superclass


I guess this question is not strictly Avro4s-specific, but here is my problem: in the following scenario

trait Event
case class UserCreated(age: Int) extends Event
case class UserDeleted(age: Int) extends Event

I would like to be able to decode one such event based on an indication, something like

def getDecoderBasedOnType(eventName: String): Decoder[Event] = 
  eventName match {
    case "usercreated" => Decoder[UserCreated]
    case "userdeleted" => Decoder[UserDeleted]
  }

This won't work because the return type of this function is

def getDecoderBasedOnType(eventName: String): Decoder[_ >: UserCreated with UserDeleted <: Event]

Is there a way to achieve my goal? Pointers for me to understand what is happening are super welcome.


Solution

  • This question is not Scala 3 specific.

    Nothing is wrong with the return type Decoder[_ >: UserCreated with UserDeleted <: Event], which is a legitimate existential type.

    But if you really want Decoder[Event], the thing is that although Decoder could be covariant (trait Decoder[+T], i.e. Decoder[A] <: Decoder[B] for A <: B)

    https://scastie.scala-lang.org/Vj9pGvRiTMaFZIwJy8Z3EA

    actually as defined it is invariant (i.e. Decoder[A], Decoder[B] are unrelated). But there is .map transforming Decoder[T] into Decoder[U]

    trait Decoder[T] {
      self =>
    
      def decode(schema: Schema): Any => T
    
      final def map[U](f: T => U): Decoder[U] = new Decoder[U] {
        override def decode(schema: Schema): Any => U = { input =>
          f(self.decode(schema).apply(input))
        }
      }
    }
    

    https://github.com/sksamuel/avro4s/blob/master/avro4s-core/src/main/scala/com/sksamuel/avro4s/Decoder.scala#L21-L24

    You can try

    def getDecoderBasedOnType(eventName: String): Decoder[Event] = 
      eventName match {
        case "usercreated" => Decoder[UserCreated].map(identity)
        case "userdeleted" => Decoder[UserDeleted].map(identity)
      }
    

    https://scastie.scala-lang.org/H5vMxWfBS8SN1FryfysFBg


    Contravariance vs Covariance in Scala

    How to check covariant and contravariant position of an element in the function?