News for hackers. Stuff that matters.
Re:How generics are implemented in Go 1.18 by klodolph on Tuesday March 01, 2022 @03:44PM (#30519740)
I only occasional do language design / compiler writing projects, or work with compiler internals.

Most people are really bad at estimating the implementation complexity and tradeoffs inherent in various language features. I'd say that C++ serves as a warning to others... think before you add features to your language, or you'll end up a total mess, like C++. Java and C# gave us some interesting and subtle lessons about what does and does not work about language features like generics. C++'s templates are just a total mess, all around. Java's generics are kind of a funny compile-time feature and have a ton of limitations. C# generics have some downright nasty interactions with other parts of the type system, like operator overloading.

IIRC the designers of C# have some regrets about how generics and other features were implemented. This is from an in-person talk, I don't have anything to cite.

You should take almost nobody at face value when they talk about language features like generics, because there is almost nobody with direct experience both designing and implementing those features. You should be pretty skeptical about what I'm saying too, IMO. But do believe that the design tradeoffs for generics are a complicated enough to justify the wait.

The Golang approach seems to strike a balance between extreme approaches. The C++ approach is "pay for what you use" which, in practice, is actually an extremist language design philosophy. In C++ / Rust, you are supposed to pay for templates only in code size and not in runtime. Everything is fully instantiated. The Haskell approach is "one abstraction fits all, everything is a pointer, use an implementation dictionary, monomorphization is an optimization". Again, something of an extremist approach (similar to Java's, but the comparison with Go / Haskell is better because Go / Haskell both use implementation dictionaries).

A middle approach is actually somewhat novel, believe it or not.

Re:How generics are implemented in Go 1.18 by shadowgovt on Tuesday March 01, 2022 @03:48PM (#30519794)
C++ still has the dubious honor of being the only language where I encountered code that I could not compile because my machine lacked enough memory.

I'm sure that's possible in other languages, but the program I was trying to compile wasn't that complicated... It had just been written by someone who believed every concrete class needed an abstract interface it was implementing.

Re:How generics are implemented in Go 1.18 by kllrnohj on Tuesday March 01, 2022 @06:32PM (#30521527)
I see you haven't worked with the joy of gradle & modern Java development. By the time you stack up gradle, javac itself, proguard or for Android R8 which are basically required to fix the terrible bytecode javac produces, some annotation processors because WHY NOT, custom build plugins, etc... it gets big
Re:How generics are implemented in Go 1.18 by vips7L on Tuesday March 01, 2022 @10:49PM (#30523425)
I highly doubt that javac produces terrible bytecode.
Re:How generics are implemented in Go 1.18 by kllrnohj on Wednesday March 02, 2022 @09:11AM (#30527459)
I highly doubt you've ever looked at it then. javac more or less intentionally doesn't do any optimizations, it leaves it all for the job of the JIT at runtime. It doesn't do either class-local optimizations nor whole-program / LTO-equivalent optimizations. Things like proguard offer both, and hence why they can so easily claim things like +20% performance. There's a lot of low-hanging fruit in javac's output, at the cost of needing a map file to reconstruct source mappings of course (although Java could of course integrate this into the class files if it wanted, it would just rather invest more or less exclusively in the JIT & GC).

Here for example: https://godbolt.org/z/4nzPY3h57 javac doesn't even bother to resolve that static field at compile time, which wouldn't even have observable side effects. Nope, just blindly emits a static initializer to invoke square(2) at runtime.

Or for a real fun one, enums: https://godbolt.org/z/xjbYvc8dx

Now some of that is necessary to handle all the stuff other classes could do with the enum, but some of it is just unnecessary. Like building the values array, which invokevirtuals all the ordinals of all the enum values. Ordinals the compiler already knew, since it's literally in the same .class file further up (and these are always in the same .class file, they are in lock-step, so there's again no observable difference if the values array was defined with constants instead of invoking ordinals on each field).

And then the trivial switch statement there isn't optimized at all, either, which again wouldn't be observable (compare against what eg. gcc would produce: https://godbolt.org/z/414rGjbE1 )

Further note that these examples are both static initializers, which means the JIT never "fixes" these (and thus instead perpetually contribute to slow startup). So if javac was going to bother with any attempt to optimize at all, you'd think it'd be here.

Re:How generics are implemented in Go 1.18 by vips7L on Wednesday March 02, 2022 @09:47AM (#30527895)
While those are low hanging fruit and I want to see more things like that done (I'd cry for more aot scalar replacement), I don't think those are examples of "bad bytecode".

Bad bytecode is the kludge of byte code you get when you try to write the same thing in kotlin: https://godbolt.org/z/9sGa6csa6

Re:How generics are implemented in Go 1.18 by kllrnohj on Wednesday March 02, 2022 @09:55AM (#30527996)
This would be the same thing in kotlin: https://godbolt.org/z/dq86ezsKd

And the bytecode is of comparable quality to javac's.

Re:How generics are implemented in Go 1.18 by shadowgovt on Tuesday March 01, 2022 @09:22PM (#30522908)
Computers seem to have caught up. The Java stack is the slowest one I regularly use (though it does support a certain amount of hot-recompile that other toolchains I use don't or can't), but it's never just crashed because it can't find room to store all those bytecode files.
Re:How generics are implemented in Go 1.18 by vips7L on Tuesday March 01, 2022 @10:56PM (#30523474)
I’ve compiled projects with javac, scalac, g++, gcc, and dmd. Javac is faster than all of them.
Re:How generics are implemented in Go 1.18 by pkolaczk on Wednesday March 02, 2022 @04:32AM (#30525491)
javac doesn't have to generate optimized machine code, which saves it a lot of time. But you pay the price incrementally in all those slow startups of java apps.
Re:How generics are implemented in Go 1.18 by pjmlp on Wednesday March 02, 2022 @08:06AM (#30526878)
Only if stuck in Java 8, those of us in modern Java use JIT caches with PGO feedback.
Re:How generics are implemented in Go 1.18 by dboreham on Wednesday March 02, 2022 @04:07AM (#30525365)
You really need Scala to run out of memory though.
Re:How generics are implemented in Go 1.18 by klodolph on Tuesday March 01, 2022 @03:51PM (#30519825)
I've done that in lots of languages, but it usually means I was trying to crash the compiler :-)

I remember there was some simple way to crash a Haskell compiler with an out of memory error by defining a series of types, where each successive type required twice as much memory as the previous type to represent.

Re:How generics are implemented in Go 1.18 by gspr on Wednesday March 02, 2022 @06:30AM (#30526143)
> I've done that in lots of languages, but it usually means I was trying to crash the compiler :-)

It's a real problem.

Anecdotally: I maintain a Debian package with a large-ish (but far from huge), template-heavy C++ codebase. I had to give up on compiling on 32 bit machines long ago, simply because not enough addressable memory was available to the compiler. I know other maintainers have to heavily tune the compiler to work around the problem, too.

I was once asked by the Ubuntu folks to disable parallel builds altogether, due to even their 64 bit builders running out of memory altogether.

And this package is just large-ish – it's nowhere near truly large (like a browser or an office suite).

Re:How generics are implemented in Go 1.18 by nosefrog on Tuesday March 01, 2022 @04:00PM (#30519933)
I wasn't able to run the Clojure compiler in 2012 on a $150 dollar chromebook because it would OOM... but I blame myself for that one :P
Re:How generics are implemented in Go 1.18 by jayd16 on Tuesday March 01, 2022 @04:51PM (#30520525)
>C# generics have some downright nasty interactions with other parts of the type system, like operator overloading.

Operators are static methods so why is that surprising or nasty?

Re:How generics are implemented in Go 1.18 by klodolph on Tuesday March 01, 2022 @05:07PM (#30520712)
It affects method overloading too, I should have said “method overloading”.

Just take a look at the rules for method overloading in C#, or take a look at one of the various C# compilers to see how methods are resolved.

Re:How generics are implemented in Go 1.18 by azth on Tuesday March 01, 2022 @09:11PM (#30522837)
Why are they an issue in C#, but not Java, which also has similar method overloading? Or are they related to default arguments?
Re:How generics are implemented in Go 1.18 by klodolph on Tuesday March 01, 2022 @11:04PM (#30523529)
You may be inferring something that I didn’t write. I didn’t once mention method overloading in Java.
Re:How generics are implemented in Go 1.18 by azth on Wednesday March 02, 2022 @02:15PM (#30531531)
Yes, I inferred because earlier you mentioned Java and C#, but only mentioned C# when it came to method overloading. Does Java have the same issue you were referring to when you mentioned C#?
Re:How generics are implemented in Go 1.18 by klodolph on Wednesday March 02, 2022 @02:24PM (#30531652)
I don't have enough Java experience to know the answer to that question.