Skip to content
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

Just-in-time-decoded types #413

Closed
wants to merge 29 commits into from
Closed

Just-in-time-decoded types #413

wants to merge 29 commits into from

Conversation

armanbilge
Copy link
Contributor

@armanbilge armanbilge commented Oct 4, 2022

This implements an alternate code generation scheme, targeting Scala 3.

Instead of generating case classes, opaque types are used that are backed by Circe Json. Then extension methods expose the fields of the type. Behind-the-scenes, these are implemented by "just-in-time" (JIT) decoding of that field to its type.

This has some limitations, including:

  • accessing a field of a JIT-decoded type may throw an exception
  • pattern matching on sum types will not work, because the underlying type for everything is Json. Anyway, opaque sum type cannot be sealed, it's not really a sum type at all anymore. This can be mitigated by adding a fold-like operation to the parent type.
  • JIT-decoded fields are not cached, they will be decoded on every access.

It should be possible to mix JIT-decoded types with members that are user-defined classes, and vice versa. This is because everything works in terms of Decoders, the only question is when the decode happens. The decoder for a JIT-decoded type is the identity function Json => Json, and further decoding is deferred till field access.

Comment on lines 205 to 211
Defn.Type(
List(Mod.Opaque()),
Type.Name(name),
Nil,
extending.fold[Type](t"_root_.io.circe.Json")(name => Type.Name(name)),
Type.Bounds(None, extending.map(name => Type.Name(name)))
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Also, bummer. Maybe add a comment with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't followed-up on the issue, looks like it may be fixed soon (or possibly already in the latest release). Will check.

@@ -497,11 +544,17 @@ trait Generator {
def valName(prefix: String): Pat.Var = Pat.Var(Term.Name(s"$prefix$name"))

val eqDef = Option.when(catsEq)(
q"implicit val ${valName("eq")}: cats.Eq[$n] = cats.Eq.fromUniversalEquals"
if (jitDecoder)
q"implicit val ${valName("eq")}: cats.Eq[$n] = _root_.io.circe.Json.eqJson.asInstanceOf[cats.Eq[$n]]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe summon[Eq[Json]] in case it changes name?

Copy link
Contributor Author

@armanbilge armanbilge Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's pretty much what I did at first 😅 the problem is, the compiler can't distinguish the opaque type from Json, so summon[Eq[Json] ends up attempting to circularly summon the very implicit we are defining 😂

in case it changes name?

This would be binary-breaking anyway, so unlikely.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I guess we could summon it once per file and assign it a name ourselves. But may be overkill.

Copy link
Collaborator

@rpiaggio rpiaggio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome, thank you.

@armanbilge armanbilge closed this Oct 5, 2022
@armanbilge armanbilge reopened this Oct 5, 2022
@armanbilge armanbilge marked this pull request as ready for review October 5, 2022 17:05
@armanbilge armanbilge linked an issue Oct 12, 2022 that may be closed by this pull request
@rpiaggio
Copy link
Collaborator

I'm going to close this issue for the time being.

@rpiaggio rpiaggio closed this Apr 16, 2023
@armanbilge armanbilge deleted the pr/jit-decoder branch August 17, 2023 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scala.js classes for results?
2 participants