trust_final_non_static_fields
Which means Java’s Record is faster, and can scala’s case class leverage that?
trust_final_non_static_fields
Which means Java’s Record is faster, and can scala’s case class leverage that?
I’m sure scala case classes have been used with reflection and mutated, which immediately disqualifies them. Scala case classes are ultimately assumed to be regular classes, so they behave as such.
Also, this is one JIT (albeit important). AFAIK graal doesn’t have this problem.
A benchmark (on all relevant platforms, e.g. JVM, Scala Native, Graal Native) comparing Scala’s case classes
to Java’s records
would be interesting nevertheless.
@MateuszKowalewski There is a benchmark in that blog and you can see the jdk’s cpp code too.
This might be the case but is also an extremely bad misuse of case class
’es (as well as case class
’es being non final by default). I mean you can do these things if you want to, but you will break properties of case class
’es that people expect, i.e. with the final
example, if you have a non final case class
you can extend it and override methods like equals
which will break unapply
.
Honestly if people are mutating a case class
by using reflection then you really should not be using a case class
and instead just a regular class. Also do note that reflection is removed in Scala 3 (and for good reason).
At least personally, I would like to see stronger enforcement on the expectations for case class
. Whether the appropriate way to do it in a way that its tied in with Java’s new records (so we kill two birds with one stone) is hard to say. It would basically mean that with JDK 21 for Scala 3 (maybe also Scala 2)? there is a new encoding if you happen to compile source code with JDK 21 but this can also be a pretty bad idea.
While I agree that modern case class idiom says not to do those things, the reality is that there’s plenty of code in the wild that does so; it’s not unusual for me to need to slap people on the wrist for using case classes in non-case-class ways.
(Especially in Scala 2, which it’s distressingly common for people to use case classes solely because they want to avoid typing new
, but in practice they are treating them like ordinary classes.)
So yes, in principle this would be nice. But it would be a major source-code-level breakage.
Of course, which is why it needs to be done properly. However I think its important to raise the remark that its a good idea to do this at some point, we just need to do it carefully.
But we can make final case class
works like Java record, or we can even get inline case class
.
We could have something like a @record
annotation that would ensure the case class is encodable (and ends up encoded) as a record in the JVM bytecode. Kotlin does something similar already (@JvmRecord
).
I know backward compatibility, but something like @record
should be imho the implicit default, and one could have some annotation to opt-out of the record encoding instead. Would be much better, imho!
It’s already super annoying that one needs to mark case classes
explicitly as final
… (Why wasn’t this actually repaired with the introduction of open
classes? Or was it and I’ve missed it?)
AFAIK making case classes records in the bytecode is an implementation detail. What about compiling to records case classes with immutable fields and keep the class implementation for case classes with mutable fields?
So this:
case class Foo(a: Int, b: String)
would be compiled to
record Foo(int a, String b) {}
And this:
case class Wtf(var what: String)
becomes the usual:
class Wtf {
//Default case class implementation
}
However, I am still worried about two concerns:
Does the compiler knows the language level it compiles to?
I think it should, via the -release flag.
Yes, otherwise, Scala will be slower than Kotlin
also discussed previously Scala Pattern Matching on Java Records
I wouldn’t worry too much about this. The performance difference is small, even for this worst-case micro benchmark.
It also doesn’t apply to Graal VM.
Finally, you can change the system-wide setting with
-XX:+UnlockExperimentalVMOptions
-XX:+TrustFinalNonStaticFields
And I expect OpenJDK to eventually make this the default (or copy Azul’s Truly Final Optimization in Zing VM | by Anna Thomas | Azul Systems | Medium )
Actually scratch that. It would be much better to have extends java.lang.Record
. That’s how it’s done with Scala’s enums which want to behave like Java’s enums, they extends java.lang.Enum[Xyz]
https://docs.scala-lang.org/scala3/reference/enums/enums.html#compatibility-with-java-enums-1
It would be much better to have
extends java.lang.Record
That’s how it’s done with Scala’s enums
That sounds like a regular way to do it. Would you like to open a SIP-proposal for this regarding extends java.lang.Record
plus rules for when it can apply and compiler error or else etc?
(This thread title could perhaps be converted to a pre-SIP then) @sideeffffect @hepin1989
I just found the jdk.internal.vm.annotation.Stable
annotation which can be annotated on fields.
but doesn’t that apply only to internal jdk stuff? jdk has more annotations like @ForceInline
but i’ve never seen them in any third-party code.
i’ve looked into javadoc for @Stable
and @ForceInline
and found that snippet:
* @implNote
* This annotation only takes effect for fields of classes loaded by the boot
* loader. Annotations on fields of classes loaded outside of the boot loader
* are ignored.
i guess that would be the subject of Project Leyden (among many other optimizations)