6 ms·
I also have a background in C/C++, etc and I've only ever found myself missing value semantics when I use languages with implicit reference semantics. I guess I
by throwaway894345 4y ago
I also have a background in C/C++, etc and I've only ever found myself missing value semantics when I use languages with implicit reference semantics. I guess I always figured the solution was "value semantics with better education / tooling". Education: people should understand value semantics. Tooling: imagine an IDE that highlights allocation points automatically (or perhaps the problem is implicit allocations rather than value semantics?).
- TremendousJudge 4y ago>or perhaps the problem is implicit allocations rather than value semantics To me, this sounds like this is it. Explicit is better than implicit is a very useful truism
- cbsmith 4y agoThe counter argument to the "explicit is better than implicit" is that abstraction & encapsulation are such significant force multipliers. If done properly, implicit is good. It's just that in case of copying, doing it "properly" is well nigh impossible.
- kazinator 4y agoexplicit implicit good * < * v v v bad * > * Good implicit is better than good explicit. (If all is good, go for implicit.) Bad explicit is better than bad implicit. (If all is bad, go for explicit; don't hide bad explicit with bad implicit.) Good explicit or implicit is better than bad explicit or implicit.
- codeflo 4y ago> perhaps the problem is implicit allocations rather than value semantics? I think that’s true. Expensive copies should never have been implicit. There was a story some time ago about a single keypress in the address bar of Chrome causing thousands of memory allocations. The culprit: lots of std::string arguments up and down the call stack. Rust gets this right, with the hindsight of C++’s example: “a = b” is a move operation by default and clone() is always explicit, except for plain data types where copying is literally memcpy — and those are clearly marked as such by the type system.
- cbsmith 4y agoIMHO, implicit allocations is a bit of a red herring. Yes, in C/C++ heap allocations are proportionately pretty expensive, but I've seen Java programs have just ridiculous amounts of implicit allocations but there really isn't much of a problem. But allocations aren't the same as copies, and the argument for reference semantics has always been that implicit copies are problematic. In your std::string example, having that many String copies in a Java program would be similarly terrible (and this sometimes happens by accident because of abstraction layers that hide all the copying going on under the covers). I do think Rust gets a lot of stuff right, but Rust's cognitive load is broadly recognized. I tend to see it as C++ with a lot fewer foot guns. ;-)
- codeflo 4y agoI’m not sure I get what you mean. You wouldn’t have that many String copies in Java by passing an unchanged String down the call stack. My point is that it’s too easy to make this mistake in C++.
- cbsmith 4y agoIn Java, the mistake happens only when there's an abstraction that hides the copying from you, so it isn't implicit in the same way, but it's still implicit.
- morelisp 4y agoAllocations are as damaging as your free function is slow. Java has a tremendously good GC, so can cope with lots of allocations. Go has an OK one, so needs some help (but mollifying it often pays dividends elsewhere in locality and memory usage too). C++ has your default system heap, good luck.
- throwaway894345 4y agoHistorically Java has traded long pause times for fast allocations, although I'm of the impression that it has recently found a way to have its cake and eat it.
- cbsmith 4y ago> I've only ever found myself missing value semantics when I use languages with implicit reference semantics. Oh, I miss it every time. ;-) I will say though that some newer languages seem to have a confused idea about how to offer mixed semantics. A bunch of them tie semantics to types. The ideal interface can vary by usage context. It's hard enough getting the semantics right as the callee (as opposed to caller), let alone when you're defining a type that will be used by who knows how many interfaces. > I guess I always figured the solution was "value semantics with better education / tooling". I've always thought much the same, but I have slowly come to appreciate that it's more than just education & tooling. Even with good education & tooling, there's a cognitive load that comes with getting interfaces right that for the general case is just not worth it.
- adgjlsfhk1 4y agoI think this is half right. For anything 64 bits or smaller, value semantics are pretty much always going to be better. That said, being able to choose between value and reference semantics for larger objects per object is a pretty useful feature.
- cbsmith 4y ago> For anything 64 bits or smaller, value semantics are pretty much always going to be better. That's assuming a 64-bit CPU (which admittedly seems like a reasonable assumption. The nice thing about the abstraction though is that there's nothing preventing the runtime from applying value semantics for those trivial small-object cases where they're obviously more efficient.
- account42 4y agoEven for a 32-bit CPU a 64-bit type is only two words to copy - and in many cases those "copies" are just register loads. In contrast, reference types means to even access it you have to read the reference and then indirectly load the memory it points to. You have to really make something contrived where a two-word type ends up being more efficient as a reference than as a value.
- Serow225 4y agoThe JetBrains IDEs can do this, at least for .NET
- pjmlp 4y agoVS did it first with Roslyn plugins.
- ok123456 4y ago> Tooling: imagine an IDE that highlights allocation points automatically Rider does this already for C#.