Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

That sounds like concepts, as used extensively in the OP.


One significant difference with concepts is that they are optional.


Gradual typing is mostly a good thing for metaprogramming I think. Even if it was just a compromise for backwards compatibility I'd still take it over C#'s rather limited generics.


I think C# is a good language, but I also am not sure that it's a poster child for this particular language feature. (I mean this literally; I don’t have enough experience to really know how it compares to the usual suspects in this area.)


.NET's and thus C#'s generics are very well designed but they rely on the design of .NET as a whole being a managed language, so it can get away with a slightly more sophisticated runtime representation.

An example of this is that generics are "reified". This is to say that you can treat each instantiation as a unique type at runtime. This is not the same as monomorphisation though, as there are rules used to promote code sharing and avoid overly aggressive specialization. For example, boxed type parameters will generally share all code paths until the JIT decides to specialize a specific call site. Value types tend to gain a lot more from immediate inlining since autoboxing (another peculiar feature of .NET) can be avoided in more cases, so these are usually specialized upon reification (much closer to what you'd see in a template, though we're still limited to type parameters here).

In Java, the lack of user defined unboxed value types makes these distinctions less attractive and thus you see they opt for type erasure and reliance on clever JIT heuristics. They do miss out on things like type-specific static class members but I've only found a few uses for this in .NET (and even then it tended to surprise folks, main case was for a type safe structured logging system with efficient runtime control of trace points).

Having said all of that, the code bloat of C++'s templates causes issues with compile time, error message comprehension, and to some degree, cache efficiency but in return the programmer gets almost complete control over all of the trade-offs mentioned above. One can have templates duplicate code, or use abstract classes as interfaces for virtual dispatch, or even a mix where the template derives shared instances for certain types. This "we can have it all" mentality is a burden that may eventually be addressed by making the common cases easier to comprehend, debug, and compile.

I'm not necessarily going to wait for C++ to change but it's interesting to watch it make its way towards new goals while other languages mature enough to replace it in certain cases (Rust obviously but there are others like Zig which I think are worth watching).


.NET also does monomorphisation when doing AOT compilation to native code, on .NET Native or Xamarin for iOS for example.


Interesting. I've been meaning to look into the AOT compilation strategies at some point. I'll have to do some comparisons with the code I'm working with.


Thanks, this matches my understanding, but I was not confident that it was correct.


A bit of a test case for me, coming from the domain of games / 3D graphics programming, as to whether a language has a 'sufficiently powerful' generics feature set is whether it is possible to implement a generic 'short vector' type as used in any type of graphics programming. I think 'good' generics / metaprogramming support should let you meet most of these goals in a generic 'short vector' type:

- Support 'n' dimensional vectors (2, 3 and 4 are the only ones commonly used in 3D graphics and games).

- Support a choice of underlying element type (at least float, double and int but it's handy to be able to support custom types like a fixed point or rational type too).

- Support operator overloading for natural expression of things like adding two vectors.

- Be very close or identical in performance to the equivalent hand written variant for every combination of dimension / element type (excluding optimizations for particular SIMD element widths etc.)

- Not be significantly worse for debugging than the equivalent hand written code (this is as much a tooling issue as a language issue).

It's possible to meet all of these requirements in modern C++ without using any particularly exotic metaprogramming functionality. It's impossible in C#. I don't know of any other language that meets these requirements as well as C++ although I'm not really familiar with the facilities offered by e.g. Rust.


Makes sense, yeah. I think that's reasonable. You can do this in Rust (https://www.nalgebra.org/vectors_and_matrices/ as an example), but one caveat: the n-dimensionality aspect isn't as nice (see the "Type-level integers" section of that doc) until "const generics" lands; that's completely orthogonal from the "generics" vs "templates" discussion, except that it's easier to implement (from the compiler's perspective) via templates.


None of these require C++'s gradual typing- the only real reason C# can't meet them is that it lacks value parameters to generics, and perhaps some limitations to its value types.


This comment wasn't about gradual typing, it was about power / generality of generics / metaprogramming support. C# fails at this in more ways than lacking value parameters and having limitations on value types.

There is no efficient and syntactically pleasant way to work with numeric types generically for example (I can't write 'a + b' and have it work for any types that provide operator+). That doesn't require C++ compile time duck typing (you could mandate something like C++ Concepts to specify a numeric interface, I believe this is sort of what Haskell typeclasses do) but it's easy, efficient and syntactically pleasant to do in C++.

The limitations of C# generics bite it in other ways too - the members of Enumerable are useful and the syntax is ok (not as nice as F# or C++ Ranges) but C# can't match the performance of C++ Ranges given the way they are implemented. They also get bitten by the operator problem - look at how Enumerable.Sum() has implementations for every built in numeric type and doesn't out of the box support your custom Numeric type.


Fair enough, typeclasses/traits were what I had in mind here. You're right that C# doesn't have a common interface to constrain your generics with for operator+/etc.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: