Use helper constructors to instantiate type classes
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 🤓
Unfortunately this is mostly useful only for Scala 3
I looked for a scalafix rule but it doesn't exist yet.
What makes this mostly useful only for Scala 3?
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
isInterfaceMemberto 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. ButisInterfaceMemberhas 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
isInterfaceMemberwe 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.
@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?
@diesalbla no, the proposal just evolved to using helper instance methods - best to take a look at #3871