Open mbore opened 3 years ago
Hi, could You elaborate on support for recursive types
and accessing annotations
?
Hi, sure:
support for recursive types
I should actually call it recursive class. I see that, there are already defined tests for them (e.g. GPerson
/RPerson
and Recursive
) but it seems that, for the following test case, the derivation doesn't work.
sealed trait Node
case class Edge(id: Long, source: Node) extends Node
case class SimpleNode(id: Long) extends Node
test("rerucsive class") {
val res = summon[Show[String, Node]].show(Edge(1, SimpleNode(2)))
println(res)
}
accessing annotations
I see, annotations are already supported, but the following test case from legacy
branch doesn't work
case class MyAnnotation(order: Int) extends StaticAnnotation
case class MyTypeAnnotation(order: Int) extends StaticAnnotation
sealed trait AttributeParent
@MyAnnotation(0) case class Attributed(
@MyAnnotation(1) p1: String @MyTypeAnnotation(0),
@MyAnnotation(2) p2: Int @MyTypeAnnotation(1)
) extends AttributeParent @MyTypeAnnotation(2)
test("capture attributes against params") {
val res = summon[Show[String, Attributed]].show(Attributed("xyz", 100))
assertEquals(res, "Attributed{MyAnnotation(0)}{MyTypeAnnotation(2)}(p1{MyAnnotation(1)}{MyTypeAnnotation(0)}=xyz,p2{MyAnnotation(2)}{MyTypeAnnotation(1)}=100)")
}
diff:
=> Diff (- obtained, + expected)
-Attributed{MyAnnotation(0)}(p1{MyAnnotation(1)}{MyTypeAnnotation(0)}=xyz,p2{MyAnnotation(2)}{MyTypeAnnotation(1)}=100)
+Attributed{MyAnnotation(0)}{MyTypeAnnotation(2)}(p1{MyAnnotation(1)}{MyTypeAnnotation(0)}=xyz,p2{MyAnnotation(2)}{MyTypeAnnotation(1)}=100)
Hi, recursive types
are indeed supported. See tests for Tree
here.
Your snippet with a small tweak works:
sealed trait Node
case class Edge(id: Long, source: Node) extends Node
case class SimpleNode(id: Long) extends Node
object Node:
given Show[String, Node] = Show.derived
test("rerucsive class") {
val res = summon[Show[String, Node]].show(Edge(1, SimpleNode(2)))
println(res)
}
In order for recursive structures to work with magnolia for Scala 3, an explicit given is needed.
Fortunately for enums and type classes with single parameters, it can be neatly expressed with just derives
clauses. Just like in the readme. So using enums is the preferred way of defining those structures in Scala 3.
I think that it's more of a documentation issue. I'll try to make it more explicit in readme.
Re: recursive types - I think we also need to have them supported if the given
is defined externally, not on the companion object. This is needed so that we can work with externally defined types.
E.g. I think typeclasses for List
should be derivable given given [T] Show[T] = Show.derived
RE: a simple way to pass configuration
- since Scala 3 adds Trait Parameters wouldn't it be enough to just add a configuration parameter to the derivation trait?
RE: a simple way to pass configuration - since Scala 3 adds Trait Parameters wouldn't it be enough to just add a configuration parameter to the derivation trait?
That could work, or it could be a method that you can override (depending on ergonomics). It would have to be inline
.
Edit: In fact it would have to be a method, because trait/class parameters cannot be inline
.
@joroKr21 @KacperFKorban Maybe I'm misunderstanding your proposal, but once the companion object extends the Derivation
trait, the parameters need to be fixed. Here, we are looking at parametrising the derivation for specific .derived
invocations.
I think we wanted to try a two-level solution, where the .derived
method is available via an extension which requires the implicit configuration; the extension then implements the Derivation
trait and has access to the parameter. But not sure if it's done in tapir, @mbore ?
Maybe I'm misunderstanding your proposal, but once the companion object extends the Derivation trait, the parameters need to be fixed.
Yeah I thought that's what we wanted (to configure the derivation itself).
I think we wanted to try a two-level solution, where the .derived method is available via an extension which requires the implicit configuration; the extension then implements the Derivation trait and has access to the parameter.
So you mean passing runtime configuration, not compile-time? I guess that is another axis of configuration but it would have to be customisable.
So you mean passing runtime configuration, not compile-time? I guess that is another axis of configuration but it would have to be customisable.
Yes, e.g. in tapir when deriving the Schema
, we pass in configuration which specifies how field names should be transformed - this can be from camelCase to snake_case etc.
I think we wanted to try a two-level solution, where the .derived method is available via an extension which requires the implicit configuration; the extension then implements the Derivation trait and has access to the parameter. But not sure if it's done in tapir, @mbore ?
Yes, you right, but we haven't implemented it yet.
Can someone elaborate about support for values classes
? What differs from Scala 2, what needs to done to support it, etc.
The problem is that value classes
don't have generated Mirror
s in Scala 3. I'm not sure if there is anything to be done here. If I remember correctly, the problem was that one of the generated methods conflicted after erasure.
The most obvious fix would be finishing #321, though I'm not sure how to tackle some of the new problems posted there.
Thanks for answer @KacperFKorban.
Is mirror-less approach needed? I wonder if something similar to this can be done here. It's a hybrid approach that piggybacks on mirrors when they can be generated and provides necessary information for other types for which there are no mirrors, like value classes.
@kpodsiad that could probably work :) maybe you'd like to try implementing this in magnolia?
Partial support for value classes (case classes, without type parameters) has been added in https://github.com/softwaremill/magnolia/pull/435
join
/split
methods (e.g. from Scala 2 - https://github.com/softwaremill/tapir/blob/master/core/src/main/scala/sttp/tapir/generic/internal/SchemaMagnoliaDerivation.scala)