-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Debug derivation #1076
base: series/2.x
Are you sure you want to change the base?
Conversation
1ef0a34
to
e770411
Compare
Derivation of instances of functional abstractions for sum and product types is great but we should not be compromising our representation of the underlying nature of these abstractions to accommodate implementation limitations of particular third party libraries. Scala 3 now supports derivation for contravariant types directly and we can pursue other solutions such as |
e770411
to
15a30e9
Compare
That makes sense. Let me checkout |
Also make Debug to be invariant or it can't derive the typeclass for sealed traits
15a30e9
to
a90083a
Compare
@adamgfraser I've tried I came to a solution by making the auto derived instance not As |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just opened a PR dusting off some of my old work on derivation now that the issue with derivation of contravariant types on Scala 3 has been resolved. That provides a very nice experience for derivation of functional abstractions on Scala 3. However, Magnolia could be useful for users who are on Scala 2 or need to cross-compile.
@@ -193,6 +193,44 @@ lazy val experimentalTests = crossProject(JSPlatform, JVMPlatform, NativePlatfor | |||
.nativeSettings(nativeSettings) | |||
.enablePlugins(BuildInfoPlugin) | |||
|
|||
lazy val derivation = crossProject(JSPlatform, JVMPlatform, NativePlatform) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would name this magnolia
as it is really providing interoperation with Magnolia and we may support interoperation with other derivation libraries or provide our own derivation solution in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem. Let me rename it.
/** | ||
* Derives a `Debug[Set[A]]` given a `Debug[A]`. | ||
*/ | ||
implicit def SetDebug[A: Debug]: Debug[Set[A]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great to add these instances! Can we add them to the tests too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Let me do it.
|
||
// need to keep it as not implicit, or the derivation would reference itself for the subtype's instances | ||
// and runs into StackOverflow in the runtime | ||
val debug: Debug[Entity] = DeriveDebug.gen[Entity] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty terrible. I think if you use -Xsource:3
it will fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me have a try and see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-Xsource:3
can't fix this either. :-(
The underlying problem of this can be implicit is because Debug
is contravariant, and Magnolia
/zio-deriving
would resolve the subtypes' typeclass instance to this exact debug instance, instead of generating a new one.
When I add the implicit
keyword under the magnolia implementation, the compiler would complain on this line that:
Implicit resolves to enclosing value debug
[warn] implicit val debug: Debug[Entity] = DeriveDebug.gen[Entity]
And during runtime, as it always refer to itself, we will get a StackOverflowException in both Magnolia
and zio-deriving
.
But if I remove the implicit
keyword, both libraries can generate a dedicated instance for a sealed trait's subtypes.
A workaround I can think of is to mimic DeriveGen
from zio-test
, making DeriveDebug
a invariant typeclass, which can provide the Debug
instance (like the invariant DeriveGen
can provide a instance for Gen[-R, +A]
). So that the client side can always declare the Debug
instance implicit
.
But the drawback of having the DeriveGen
style implementation is that it can't provide very meaningful information, when it can't generate an instance. It's a painful experience every time I need to figure out what I miss if a hierarchy when it can't generate a DeriveGen
.
How do you think about this solution?
object Entity { | ||
case class Person(name: String, address: Address) extends Entity | ||
object Person { | ||
implicit val debug: Debug[Person] = DeriveDebug.gen[Person] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to have an annotation for this. Something like @deriving(Debug)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate more on this? Do you mean to have some annotations like @jsonHint
so that we can fine tune the generated Debug instance?
Debug
is a nice feature, but without the auto derivation, it's not very practical to use it in a project.This PR adds the support for auto derivation through magnolia. But due to the limitation of magnolia, it requires the type parameter in Debug to be invariant. Which is a drawback of adding the support.