Hacker News new | ask | show | jobs
by valenterry 1874 days ago
You are supposed to create a new copy with some of the fields changed. Just like you do it with the java.time.* classes and others.
2 comments

Yet the only mechanism for this is error prone or relying on mountains of boilerplate.
Instead of changing few bytes, now I have to copy hundreds of bytes around and add more stuff for GC to collect.

Well, they have to obey Wirth's law, I guess.

I mean, you do realize that you're writing Java code right? If you want ultimate low level optimization and control then you're already about ten miles too far downstream to make that turn. Also, I'm guessing the copy overhead is more than offset by the JVM being able to do better optimizations around these data structures.
Structs with mutable fields is hardly “ultimate low level optimization”, it’s something a lot of programmers still use as a matter of course. (I say this as a fan of immutable data!)

Ultimate low-level optimization in Java would be more like packing your structure into arrays of integers - which is something people actually do in Java. Just because you’re using Java doesn’t mean you don’t want your code to run as fast as possible.

I don't know if the JVM developers have actually implemented this, but since records are immutable, there's no reason why a copy couldn't share memory between the instances.

The major downside is that could ruin cache locality and make passing the instance across a FFI boundary require a copy.

Another optimization would be that the JIT (or even perhaps javac) could notice cases where you make a copy (with one field changed) of a record and then never use the original reference again. If the JIT (or javac) can prove that no other bit of code holds a reference to the original record, it can reuse and mutate the original one instead of making a copy. I don't know if this optimization is or will be implemented, of course.

Either way, I expect the overhead you mention ends up being worth the benefits of immutable data. (That's been my experience using Scala, anyway.)

This has been argued ad nauseam around the time Scala started gaining traction because scala's collections are grouped into immutable and mutable. The consensus at the time is that the GC overhead is well worth the ability to parallelize computation.
You have to copy less than you think. Because the data is immutable, you can share everything but the changed fields.
Then you've invented mutability without references/identity. Except those are desired properties for data classes, unlike for value classes which have such semantic difference.

Btw Kotlin allow to make immutable Java records too so clear winner.

Not sure what you're getting at. Immutable means exactly that. If you hand out an object, then do a copy change, that change isn't reflected in the object you gave to another method/thread/fiber/etc. Immutability doesn't mean application state never changes; it means that a single reference will always point to memory that hasn't changed.
Parent means basically the difference between identity and primitive/value classes. In Haskell, you’ve only got the latter (maybe you can manage something with lower level controls exposed), that is in a non-haskell syntax new Point(3,2) == new Point(3,2), even though in memory the two object is different.

“Problem” is with records, that they are only shallowly immutable. record Rect(List<Point> corners) will readily hand out a “pointer” to a mutable list. It can be solved of course by storing only immutable objects.

What parent may have failed to get from grandparent comment is that the latter likely meant it under the hood, transparently to the user. That is, new Point(3,2) != new Point(3,2) but the JVM can make the object reference the same data, because the field itself is final. Thus a copy can be optimized at the JVM level, while still having identity.

Or you know, the JIT will trivially optimize away the old class if it is reassigned to the same variable, as you would use it inside a loop. How do you think the litany of FP languages work? Like Haskell, Scala, Clojure?
It can be done in theory, but the JVM does nothing of the sort right now.
False. The JVM already does quite a good job with escape analysis, and record types just add extra semantic information to potentially further improve the situation.
Counterpoint: Java programs with memory consumption graphs that have decided sharktooth patterns, which is extremely common.
As opposed to what exactly? How would a C program’s memory graph look with quick bursts of memory-allocation requiring functionality, especially if it is very dynamic in nature? Yeah you can overallocate with memory pools and the like, and there are cases of course where escape analysis can’t help — that’s why Valhalla is in the works for quite some times now.

But GC-wise the JVM is far ahead the game, whatever you see is likely better than the same functionality would be under JS, Python, C#, Go, etc (though the latter two do have value types/structs already so they can at some place manually do the “escape-analysis”. But not every problem requires/can use value types either)

What precisely of what I said is “false”?
the JVM does something of the sort right now.
That's a theory not happening in practice. In practice Java programs are slow and memory-hungry because of those issues when some people think that it's cheap to create small objects or that escape analysis will solve their issues without verifying that it works for their case.
Most of the world’s server applications would like to disagree with you.
Java isn’t perfect. But you underestimate the amount of software written in it. Or even things like Python and JS which are a lot more basic but have similar elements in regards to their memory model. What do you use?
I work as Java developer for the last 10 years, I perfectly understand the amount of software and other things.

I don't know much about Python and JS, but I do know that they're not using immutable model, everything is mutable in Python and in JS, so I'm not sure what's your point. The only immutable language I'm aware of is Haskell which is not used widely. Just because JVM is faster than Python or V8 does not mean that it's OK to slow it down with immutables.

Using immutables doesn’t mean you go full Haskell. Strings are also immutable you know.
GC is there for a reason, unless you do HFT, use it to your advantage.
You write java and worry about bytes copied?
Yes, I do. Java could be quite fast if you won't slow it down on purpose.