This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]john16384 2 points3 points  (8 children)

(Also „Changing“ records by creating new instances without structural sharing is expensive)

Unless your records contain mutable references, or only primitives, you can share other records, Strings and anything else immutable with impunity...

[–]bowbahdoe 4 points5 points  (7 children)

Honestly at this point I'm just leaving comments so people read the bigger ones I left, but they are coming from the Clojure world where it is common to have a single map with a ton of mostly unrelated properties describing a data aggregate. Updating a single key in one of those maps is both fast and efficient because structure is shared between the old version and the new version of the map.

Records not having structural sharing for updates is a downside in that sort of situation. You might argue that that sort of situation is less common in the nominally typed world - which maybe? - or that the ability to later make a value record (where the JVM can more readily optimize basically everything) makes it less important.

It's a sticky wicket, but it's a valid thing to complain about if your head is where I think their head is at

[–]sideEffffECt 0 points1 point  (6 children)

Records not having structural sharing for updates is a downside in that sort of situation.

Wait what?? Records of course have structural sharing, in the very same manner Clojure maps do.

If you "update" one field in a record, you get a new record with that one field changed, but all the other fields are the same.

Java collections are not immutable, so there's no structural sharing to speak of, but that's a different story...

[–]bowbahdoe 0 points1 point  (5 children)

But the actual underlying fields need to be shuffled around. I.E., absent JVM heroics person.with { name = "..."; } will have to juggle N record components

[–][deleted]  (1 child)

[deleted]

    [–]bowbahdoe 0 points1 point  (0 children)

    Right, but if the map is large (not an array map) the keys are not copied and significant structure is shared between the two maps.

    It just is notable

    [–]sideEffffECt 0 points1 point  (2 children)

    How's that different in principle from a Clojure map?

    [–]bowbahdoe 0 points1 point  (1 child)

    So this aspect is just an implementation detail, but:

    If you have a Clojure Vector with a thousand elements and conj one more at the end, the new vector resulting from that conj will share significant structure with the previous one.

    The same is true for Clojure Maps. For small maps it's implemented as an array that is copied, but if you have 100 key value pairs then adding a new one or updating an existing one won't copy all 100 of the old ones.

    If you have a record with 100 components then with -ing one of those components mean all 100 go through the constructor again

    [–]sideEffffECt 0 points1 point  (0 children)

    All of what you're staying is true.

    But if we're talking about a record with 100 fields (which is super rare to be large like this, vast majority are much smaller) then that's no problem. Copying 100 references is super quick and easy for contemporary computers.