Skip to content

Rethinking type class boilerplate generation #3192

@travisbrown

Description

@travisbrown

This issue is a follow-up to #2553 that focuses on the Simulacrum question and gives a concrete proposal for a replacement.

The problem is that Simulacrum 1 is based on Macro Paradise's macro annotations, which means it has a couple of disadvantages:

  • It doesn't work on Dotty, which doesn't have macros that can do this kind of thing (and probably never will, as far as I can tell).
  • It can be difficult to troubleshoot when it doesn't do what you expect (you have to turn on tracing and dig through some pretty verbose output).

I've been experimenting with reworking Simulacrum's code generation as a set of Scalafix rules. This Scalafix-based approach resolves both of the macro-related issues:

  • It works exactly the same on Dotty as on Scala 2.
  • The generated code is right there in front of you.

This second part is also a disadvantage: in my current proof-of-concept you end up with a lot (like 2.2k lines) of boilerplate being added to the Cats source. It "reserves" the last part of the companion object of every type class for rule-managed code, and if contributors accidentally edit this part of these files, their changes will be overwritten.

I've tried a few ways of generating at least some of the syntax method boilerplate as managed source, in order to minimize the additions to checked-in code, but I don't think it's possible to do this in a way that makes sense without breaking binary compatibility. This might be a viable solution for Cats 3, though, and it would be pretty straightforward to change my proof-of-concept to make this configurable (i.e. your build indicates whether the generated code should go behind a banner in src/main/scala or in src_managed).

The alternative seems to be to implement the current Simulacrum functionality either as a Dotty compiler plugin or directly in the compiler.

If we do decide to go the Scalafix route, there's also a question of when. In theory it could happen at any time, since whether the code generation is done by a macro or a Scalafix rule shouldn't make any difference to the published artifacts.

Simulacrum isn't the only thing blocking cross-building on Dotty, though—the use of kind-projector is also a problem. My proof-of-concept also includes rules for rewriting kind-projector's syntax, but only for the sake of experimenting with Dotty cross-building (the result is an awful mess and not something I'd ever want to merge).

So personally I think we should switch out Simulacrum 1 with a Scalafix-based approach sooner rather than later, but we're still going to have to come up with a Dotty-friendly way to write type lambdas and polymorphic function value definitions before we can start cross-building.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions