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

Clarify semi- and auto- derivation for jsoniter-scala #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

plokhotnyuk
Copy link

@plokhotnyuk plokhotnyuk commented Sep 20, 2024

I haven't measured compilation and runtime scores yet...

I bet that difference of results between these semi- and auto- will not be huge.

@lbialy
Copy link
Collaborator

lbialy commented Sep 20, 2024

I thought auto is when there's a implicit def derive[A]: Instance[A] providing intermediate instances for the, usually macro-driven, derivation mechanism for products and coproducts? That's what auto in circe does and I think this is the meaning in which @MateuszKubuszok is using it here.

@MateuszKubuszok
Copy link
Owner

TBH what I want to show with my incoming presentation is that:

  • majority of libraries have a very specific definition of auto and semiauto:
    • auto is always recursive, does not require implicit val foo = ...
    • semi is never recursive, requires implicit val foo = ...
    • semiautomatic derivation with recursion require either all intermediate values derived manually, or import of both auto and semiauto present when deriving implicit value
  • what Jsoniter does is, from the POV of these definitions
    • NEITHER truly automatic - it does require at least 1 manual implicit val foo = ...
    • NOR truly semiautomatic - it does not require manual handling of intermediate derivations nor some autoderiving import
    • it's something in-between - recursive-semi-automatic derivation, which allows better: compile times, runtime performances and error diagnostics than the standard auto/semiauto
  • additionally, I want to present a pattern which allows making this approach automatic
    • without creating intermediate type classes by autoderivation
    • only using type class instances provided by user explicitly

so basically something like:

imports

// no manual implicits with semiautomatic derivation

value.extensionMethod[A] // using TypeClass[A]

which would summon

new TypeClass[A] {
  
  def method(args) = {
    def a(..) = ...
    def b(..) = ...
    def c(..) = ...
    ...
    z(args)
  }
}

so that the code could be both optimal AND easy to use.

@lbialy
Copy link
Collaborator

lbialy commented Sep 20, 2024

wouldn't such automatic-semiautomatic approach inline a separate instance at each extension call site?

@MateuszKubuszok
Copy link
Owner

MateuszKubuszok commented Sep 20, 2024

If you have several different call sites, asking for the same type, then yes, you would derive the same instances several times anew

BUT

  1. (recursive) semiautomatic derivation would still be a thing, if you need it
  2. measuring the compilation time shows, that by not-playing a ping-pong with type checker, a single derivation (which inlines everything into a single type class instance) can take only half the compilation time of a normal semiautomatic derivation, and a fraction of time of automatic derivation, so it's become a problem much, much later
  3. in codebases when most derivations are unique to a single use site (e.g. you derive it once, to consume by Tapir, or sth) it saves a lot of effort

so it would be a significant improvement over the current status quo when people are resorting to semi-auto not as a measure of correctness/coherence enforcement, but a way of optimizing around long compilation times. And, when semautomatic derivation would be actually needed, it would allow them to not-de-optimize their code by introducing a bunch of allocation for intermediate instances that aren't really needed.

Also, it lowers the entry barrier, as newbies don't have to wonder why all the senior people write 50 implicit defs not used outside the file they are defined in, and they cannot explain why it's actually necessary.

@plokhotnyuk
Copy link
Author

plokhotnyuk commented Sep 20, 2024

Yet another drawback of implicit def and inline given methods that they multiply number of created type-class instances, like here.

@lbialy
Copy link
Collaborator

lbialy commented Sep 20, 2024

Yeah, the argument against auto is quite clear and there's no disagreement here. It's just that what you do here in this PR @plokhotnyuk is what @MateuszKubuszok describes as recursive-semi-automatic derivation so not really auto, more like "semi-auto done correctly".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants