Compile error when deriving a schema in Scala 3, which works in Scala 2

Hi,
I’m currently checking if my Scala 2 project (which uses Tapir) can be migrated to Scala 3.
I’ve reached one compiler-error, which I don’t fully understand, and I hope maybe someone here could help me with understanding it.

My API uses a 3rd-party library called Diffson JsonPatch, which provides a sealed type for defining patch-operations:

sealed abstract class Operation[Json: Jsony]

case class Add[Json: Jsony](path: Pointer, value: Json) extends Operation[Json]
case class Move ...
case class Remove ...

My Scala 2 code includes a manual definition for Schema[Operation[JsValue]] which is manually built (with some helpers), and relies on the Schema.derived method for all the sub-types:

implicit lazy val jsonPatchOperationSchema: Schema[Operation[JsValue]] =
    Schema
      .any[Operation[JsValue]]
      .name(SName("JsonPatchOperation"))
      .discriminatorKey("op")
      .addSubType("add", Schema.derived[Add[JsValue]].name(SName("JsonPatchAdd")))
      .addSubType("move", ...)
      ...

where discriminatorKey and addSubType are my custom helper methods, but that does not matter because it’s not the cause of the issue.

The problem now is that Scala 3 compiler does not like the Schema.derived[Add[JsValue]] call:

No given instance of type scala.deriving.Mirror.Of[diffson.jsonpatch.Add[play.api.libs.json.JsValue]] was found for parameter m of method derived in trait SchemaMagnoliaDerivation. Failed to synthesize an instance of type scala.deriving.Mirror.Of[diffson.jsonpatch.Add[play.api.libs.json.JsValue]]:
	* class Add is not a generic product because it takes more than one parameter list
	* class Add is not a generic sum because it is not a sealed class [41:54]

So, I understand and agree with the error statement - Add is not a sealed class itself, and it has 2 parameters, which is more than 1.
But why there are those constraints for Mirror.Of hence for Schema.derived?
And how can I overcome it and derive a schema for a case class with a generic type and more than one param?
Thanks in advance.

ok, I think I’ve found at least half of the answer - the problem is that the generic type of the operation has the implicit itself: Operation[Json: Jsony]
I tried to replicate this problem with my custom types, and as soon as I add the context bound - I’m getting the same error. So Mirror.Of can not be resolved for a case class with a generic type with a context bound.
Here is the minimal code which reproduces it:

  trait Context[T]
  case class Foo[T: Context](v: T)

  summon[Mirror.Of[Foo[Int]]] // < error

and even after manually injecting the implicits for context - it still produces the error:

  trait Context[T]
  case class Foo[T: Context](v: T)

  given Context[Int]            = ???
  given Mirror.Of[Context[Int]] = ???

  summon[Mirror.Of[Foo[Int]]] // < error

ok, it seems it’s just a problem of Scala 3, or rather me missing some knowledge of Scala 3, and it has nothing to do with Tapir.
I’d say the question is closed. Although if someone knows the answer why context bound type params might not work with Mirror.Of - I’d appreciate the help :heart:

ahh, ok! I think I’ve got it - since Context[T] (or Jsony[T] from the original question) is just a simple trait - then corresponding to the Scala 3 docs that’s exactly the case when the Mirror.Of can’t be auto-resolved.
I will follow the Scala 3 docs further and see if I find a solution for this scenario.

1 Like

Unfortunately I don’t know the answer to your question, but I appreciate the detailed report :slight_smile: If there’s some type for which it’s hard/impossible to derive a schema, create a bug in Tapir, maybe we’ll be able to do something with that