4 ms·
> Looks very similar to Go generics, which also don't support monomorphization. That's not true. Go's generics are monomorphized, when it makes sense (differen
by mseepgood 2y ago
> Looks very similar to Go generics, which also don't support monomorphization.
That's not true. Go's generics are monomorphized, when it makes sense (different shapes) and not monomorphized when it doesn't (same shapes). It's a hybrid approach to combine the best of both worlds: https://go.googlesource.com/proposal/+/refs/heads/master/design/generics-implementation-gcshape.md https://go.googlesource.com/proposal/+/refs/heads/master/des...
- cherryteastain 2y agoGo does monomorphization in a rather useless manner from a performance perspective because all pointers/interfaces are essentially the same "shape". Hence you still have no inlining and you do not avoid the virtual function when any interfaces are involved as detailed in https://planetscale.com/blog/generics-can-make-your-go-code-slower https://planetscale.com/blog/generics-can-make-your-go-code-... C++ fully monomorphizes class/function templates, and therefore incurs 0 overhead while unlocking a ton of inlining, unlike Go.
- tialaramex 2y agoYup. The most classic example of this is that in both Rust and C++ we can provide a method on some type which takes a callable and instead of a single implementation which runs off a function pointer, the compiler will separately monomorphize individual uses with lambdas to emit the optimised machine code for the specific case you wrote often inline. For example Rust's name.contains(|c| c == 'x' || (c >= '0' && c <= '9')) gets you code which just checks whether there are any ASCII digits or a lowercase latin x in name. If you don't have monomorphisation this ends up with a function call overhead for every single character in name because the "shape" of this callable is the same as that of every callable and a single implementation has to call here. Is Go's choice a valid one? Yes. Is it somehow the "best of all worlds"? Not even close.
- foldr 2y agoThis is just inlining of a function/method parameter. It only has anything at all to do with monomorphization in Rust because lambdas have their own special unwritable types. A good modern C compiler will be able to inline calls to function pointer arguments without doing monomorphization (example here: https://news.ycombinator.com/item?id=31496445 https://news.ycombinator.com/item?id=31496445). I doubt the Go compiler does such aggressive optimization, but there is nothing to stop it in principle. Edit: I think the Go compiler might actually be smart enough to inline in this kind of case. See line 113 of the assembly listing here: https://godbolt.org/z/8qGdzeKcG https://godbolt.org/z/8qGdzeKcG
- tialaramex 2y agoIn C++ the lambda function also has an unnameable type, indeed unlike Rust that's a unique property, all of Rust's functions have such types whereas in C++ the named functions have a type based on their signature. Do you have an example of a "good modern C compiler" which inlines the function? Presumably this is a magic special case for say, qsort ?
- foldr 2y agoI linked an example here in an edit to my original post: https://news.ycombinator.com/item?id=31496445 https://news.ycombinator.com/item?id=31496445 The broader point is that compilers (including the Go compiler, it seems) can and do make this kind of optimization via inlining, and that this doesn't have much to do with any particular approach to generics.
- tialaramex 2y agoThe example seems to just be a boring case where the compiler can compute the answer for your entire program, which isn't interesting at all. Maybe you linked the wrong example?
- foldr 2y agoIt can compute the answer because it’s able to inline the function call made via the function pointer argument to 'find'. But if you don’t like that example for some reason, look at the Go example I linked above.
- neonsunset 2y agoYup, much like Rust and C# (with structs).
- jchw 2y agoGo developers didn't really want C++ templates. C++ templates are very powerful, but while they unlock a lot of runtime performance opportunities, they are also dog-slow for compilation. What Go developers wanted was a solution to the problem of having to repeat things like simple algorithms (see e.g. slice tricks) once for each type they would ever need to operate on, and also a solution to the problem of confusing interfaces dancing around the lack of generics (see e.g. the standard sort package.) Go generics solve that and only that. Every programming language has to choose how to balance and prioritize values when making design decisions. Go favors simplicity over expressiveness and also seems to prioritize keeping compile-time speeds fast over maximizing runtime performance, and their generics design is pretty consistent with that. The Go generics design did not win over anyone who was already a Go hater because if you already didn't like Go it's probably safe to say that the things Go values most are not the things that you value most. Yes, everyone likes faster compile times, but many people would prefer faster runtime performance. Personally I think that it's worth balancing runtime performance with fast compile times, as I prefer to keep the edit-compile-test latency very low; that Go can compile and run thousands of practical tests in a couple seconds is pretty useful for me as a developer. But on the other hand, you may feel that it's silly to optimize for fast compile times as software will be ran many more times than it will be compiled, so it makes sense to pay more up front. I know some people will treat this like there's an objectively correct answer, even though it's pretty clear there is not. (Even calculating whether it makes sense to pay more up front is really, really hard. It is not guaranteed.) So actually, I would like type erasure generics in C. I still write some C code here and there and I hate reaching into C++ just for templates. C is so nice for implementing basic data structures and yet so terrible for actually using them and just this tiny little improvement would make a massive, massive difference. Does it give you the sheer power that C++ templates give you? Well, no, but it's a huge quality of life improvement that meshes very well with the values that C already has, so while it won't win over people who don't like C, it will improve the life of people who already do, and I think that's a better way to evolve a language.
- foldr 2y agoYou can use value types rather than pointers/interfaces if that's what you want.