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

Use helper constructors to instantiate type classes #3870

Closed
joroKr21 opened this issue Apr 26, 2021 · 6 comments · Fixed by #4279
Closed

Use helper constructors to instantiate type classes #3870

joroKr21 opened this issue Apr 26, 2021 · 6 comments · Fixed by #4279

Comments

@joroKr21
Copy link
Member

Since we don't support Scala 2.11 anymore this is an option.
See https://www.scala-lang.org/news/2.12.0/

Scala and Java 8 interop is also improved for functional code, as methods that take functions can easily be called in both directions using lambda syntax. The FunctionN classes in Scala’s standard library are now Single Abstract Method (SAM) types, and all SAM types are treated uniformly – from type checking through code generation. No class file is generated for a lambda; invokedynamic is used instead.

"No class file is generated" sounds pretty good to me 🤓

@joroKr21
Copy link
Member Author

Unfortunately this is mostly useful only for Scala 3

@joroKr21
Copy link
Member Author

I looked for a scalafix rule but it doesn't exist yet.

@rossabaker
Copy link
Member

What makes this mostly useful only for Scala 3?

@joroKr21
Copy link
Member Author

joroKr21 commented May 3, 2021

What makes this mostly useful only for Scala 3?

There is a problem in Scala 2 for SAM traits with method implementations (i.e. having concrete methods that derive from the single abstract one which all type classes in Cats do):

  • Methods implemented in traits that can be translated to interfaces become default methods in the byte code. But the compiler also uses a method called isInterfaceMember to determine if a trait needs an initializer. The initializer is normally used to initialize trait vals and vars and also to run any statements in the body of the trait. But isInterfaceMember has a bug and it says that a non-abstract method is not a legal interface member definition (obviously false). In this case an empty initializer is generated.
  • Later in the "delambdafy" phase which translates anonymous functions to methods the compiler also takes care of eliminating generated classes for SAM expressions. But it must be careful to preserve semantics (i.e. if there are vals / vars / statements in the trait we cannot avoid instantiating a class). It does that by checking for the existence of an initializer (it must also work under separate compilation). Due to the bug in isInterfaceMember we now have an empty initializer that tricks us into believing this trait is not a pure interface.
  • This bug with generating unnecessary initializers is fixed in Scala 3 but can't be fixed in Scala 2 due to binary compatibility guarantees.

Anyway long story short, I found it's anyway good to use instance constructors because they can also work for type classes with multiple abstract methods. See the linked PR: #3871

The trick with the instance constructors is that we are passing one function at a time as arguments and they are guaranteed to be delambdafied and not generate class files.

@diesalbla
Copy link
Contributor

@joroKr21 From the last message, can we understand that you would like to withdraw this proposal? Is this something that should be reconsidered once we deprecate Scala 2.13?

@joroKr21
Copy link
Member Author

@diesalbla no, the proposal just evolved to using helper instance methods - best to take a look at #3871

@joroKr21 joroKr21 changed the title Use SAM for instances with single abstract methods Use helper constructors to instantiate type classes May 18, 2021
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 a pull request may close this issue.

3 participants