8

I'm trying to get a basic upickle example to work and it seems I'm missing something here. I want to try out the example provided on the readme page for upickle

import upickle._

sealed trait A
@key("Bee") case class B(i: Int) extends A
case object C extends A

Then, my code is:

object Model {
  def main(args: Array[String]): Unit = {

    val a = B(5): A
    println(a)

    val out = write(a)
    println(out)

    val a2 = read[A](out)
    println(a2)

    println(a == a2)

  }
}

All I get is the error:

The referenced trait [[A]] does not have any sub-classes. This may happen due to a limitation of scalac (SI-7046) given that the trait is not in the same package. If this is the case, the hierarchy may be defined using integer constants.

I have two questions:

  • How can I convince uPickle that the trait is in the same package? (Because it is.)
  • Or if I can't: how can I define the hierarchy with integer constants?
Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234

2 Answers2

8

Ever had the syndrome that you can spend multiple hours on a problem like this, and you solve it minutes after asking the StackOverflow question?

It turns out that due to compiler-specific details, such a sealed trait won't know its direct subclasses until after the point in the file where it's defined. So, in this case, I had defined the trait and its cases after the main method, where upickle would do its macro expansion and magic. At this point, it won't know about the trait's cases. If the main method is moved in the same file after the definition of the trait and its cases, it will work.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • 1
    My code was working fine until I renamed the package my trait/subclasses were in. For some reason they must've compiled in a different order. I was seriously scratching my head. Thanks for this. – Mike McFarland Jun 18 '15 at 19:41
2

I encountered this error in a Scala.js project where I shared a sealed abstract class between server and client.

The solution was to use the Typelevel compiler instead of the default Lightbend one.

This is easily done:

  1. Put this in your build.properties: sbt.version=0.13.13-M1
  2. and this at the top of your build.sbt: scalaOrganization in ThisBuild:= "org.typelevel"

For completeness' sake, this is the class I can now share between client and server:

sealed abstract class ServerResponse

case class Ok(msg: String) extends ServerResponse

case class Failure(msg: String) extends ServerResponse
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171