Earlier quoted context omitted.
It makes code unreadable. void lol >, E>()
That isn't even close to valid C++.
template void lol>, E>()
251–260 of 261 posts
Earlier quoted context omitted.
Scheme?
I fleshed my comment out into a blog post yesterday, which got some interest[0] on Lobsters. It appears that modern Scheme is easier to write portably and an even nicer compromise than I had originally thought. [0]: https://lobste.rs/s/ytuyya/perfect_language_why_go_still_isn...
(Note: Macros, even Scheme's hygenic ones, can produce horrible monstrosities. Like loop. And the urge towards object systems gives Scheme a horde of them, mostly bad. This may be a lesson in getting what you ask for.)
Maybe this is unfair, but it’d be great if the Go devs could just say “generics will work similarly to [C# / Java / Swift / D / whatever], except that we’ll address [problems] with [adjustments]”. Rather than going through this whole rigmarole of resisting adding generics too early because all existing implementations are bad, then slowly reinventing the wheel from scratch, then finally ending up with something prett…
> if the Go devs could just say “generics will work similarly to [C# / Java / Swift / D / whatever], except that we’ll address [problems] with [adjustments]”. This is basically how C++ was designed, and it turns out not to work very well; the [adjustments₁] for [feature₁] turn out to introduce not only unanticipated [problems₁] with [feature₁] itself but also new and previously unimagined [problems₂] with [feature₂].…
Earlier quoted context omitted.
> if the Go devs could just say “generics will work similarly to [C# / Java / Swift / D / whatever], except that we’ll address [problems] with [adjustments]”. This is basically how C++ was designed, and it turns out not to work very well; the [adjustments₁] for [feature₁] turn out to introduce not only unanticipated [problems₁] with [feature₁] itself but also new and previously unimagined [problems₂] with [feature₂].…
I don’t agree with your point, but I applaud your use of Unicode.
Or constructors combined with static objects; you get the static initialization order fiasco.
Or constness with template functions; you get two, four, or eight copies of each generic function in your source code according to which things are const.
Or (compile-time) overloading with (run-time) overriding; you get C++’s weird “hiding” rule about the other overrides you didn't override.
Or separate compilation with implicitly instantiated templates; you get geological build times as the compiler instantiates the same templates in every .C file and then throws away all but one of the identical instantiations at link time. (To be fair, this is far from the only reason C++ compiles slowly.)
Overriding combined with type conversions through implicitly invoked constructors (and implicit referencing and implicit casting to const) gives you annoying bugs that are unnecessarily hard to figure out.
Cleanup from exceptions via RAII combined with C’s unspecified argument evaluation order led to a situation where resource leaks during certain kinds of operations couldn't be avoided reliably, a bug in the language definition that wasn't noticed for several years, though I think it's fixed now.
The grammar is undecidable because of the number of different things that have been added, which sounds like a hyperbolic joke but is actually literally true, and a significant obstacle to implementing something like gofmt for C++.
The combination of template parameters using for parameters, the traditional longest-leftmost tokenization rule, and the >> operator for bitshifting made foo> an unexpected syntactic pitfall, one that is now fixed.
There wasn't an exception-safe version of the STL for a number of years, which isn't really an interaction between templates and exceptions —a non-template container had to deal with the same exception-safety problems— but it did mean that fit quite a while you could use the STL or exceptions but not both.
This is far from the extent of the problem. The C++ FAQ consists largely of affirmations of the form, “Doing [reasonable thing 1] works, and [reasonable thing 2] works too, but if you do them both, you will die horribly for your immorality.” It's exaggerating about the death part but the surprising problems are quite real.
I don't hate C++, and I think it's the best existing language for some problem spaces, but it's very much the poster boy for unexpected problems arising from interactions between features.
As far as the keyboard goes, https://GitHub.com/kragen/xcompose and http://canonical.org/~kragen/setting-up-keyboard are your friends.
Earlier quoted context omitted.
I don’t agree with your point, but I applaud your use of Unicode.
Well, consider C++ inheritance combined with object nesting; you get slicing copies. Or constructors combined with static objects; you get the static initialization order fiasco. Or constness with template functions; you get two, four, or eight copies of each generic function in your source code according to which things are const. Or (compile-time) overloading with (run-time) overriding; you get C++’s weird “hiding”…
It’s good that they’re being careful and conservative; but I feel that they’re going out of their way to avoid all existing paths, whereas it would actually be safer to use some existing system(s) as a starting point, as both the advantages and disadvantages -- in practice, not just theoretically! -- are known.
I definitely agree that interactions between features need to be carefully thought through. One likely wart with the current proposal is that user-defined generic types still won’t look or behave quite like the built-in ones, as those have special syntax and special operators. Will it be considered good practice to expose raw slices, maps etc in APIs? Or will people start wrapping them (just as in Java, an array would most often be wrapped in an ArrayList for convenience)? This might have been easier to resolve if generics had been thinkable much earlier in the language design.
Earlier quoted context omitted.
Well, consider C++ inheritance combined with object nesting; you get slicing copies. Or constructors combined with static objects; you get the static initialization order fiasco. Or constness with template functions; you get two, four, or eight copies of each generic function in your source code according to which things are const. Or (compile-time) overloading with (run-time) overriding; you get C++’s weird “hiding”…
I generally agree with that characterisation of C++, but it’s not at all what I was trying to get at by suggesting that Go generics should copy ideas from other languages. It’s good that they’re being careful and conservative; but I feel that they’re going out of their way to avoid all existing paths, whereas it would actually be safer to use some existing system(s) as a starting point, as both the advantages and dis…
I agree that, with this design, it won't be as comfortable to use generic containers from a library as to use the built-in containers. That's been a persistent problem with C++ templates and Java generics, too, though (I think reasonable initializers for vector finally landed in C++17?) so maybe the lesson they took was that the base language should have slices and maps because there was no known reasonable design that allowed them to be seamlessly defined in a library while supporting static typing and object nesting? Maybe there is a solution if you design the language around generic containers from the beginning—have you tried D?
Earlier quoted context omitted.
> The only truly unforgiveable one is UnsupportedOperationException. The guy who wrote the collections API for Java didn't know the first thing about the Liskov Substitution Principle, and gave the world an implementation that violates it... The implementation perfectly follows it because the LSP simply requires identical behavior, and the contract clearly states it may[1] throw an exception. All the implementations…
No they most definitely are not. One implementation receives a message. The other categorically rejects it. I cannot substitute them.
It is correct to the letter of LSP, but not the spirit of it, and this was a conscious decision; contrary to your original claim, they understood the principle perfectly well.
Earlier quoted context omitted.
Every project I’ve written so far in golang could benefit from generics.
but what is the benefit? How does your code change. Generics are a tail abstraction. Its the last abstraction that you are able to make to your code to reduce boilerplate. This usually means that is the least useful and in generics specific case the boiler place it reduces is minimal. Its lack of impact is why I dont really care about the subject. Adding or removing generics to a project matters little. So why bother…
This is absolute and utter delusional nonsense.
Earlier quoted context omitted.
I think it is Gilad Bracha that said, when the decision was made to include only covariance in Dart, that variance flies in the face of programmer's intuition. He's right. It's easy to understand one level of variance: you can replace a return type by a subtype and a parameter type by a supertype. (I wouldn't be surprised that many programmer don't undestand this.) Two levels already requires some deep thinking (assu…
I'm not really sure what you mean. Variance just changes what is and is not a subtype/supertype. If List is covariant then List is a subtype of List if T is a subtype of U. So then, by induction, List > is a subtype of List > if T is a subtype of U. I'm not sure what's hilariously hard to follow here...
Nevertheless, I have a point because:
1. Things get harder with contravariance. 2. Things get harder when you mix covariance and contravariance.
The prototypical covariance class is Producer (with method produce() returning T) while for contravariance that's Consumer (with method consume taking a T as parameter).
Assume each class has a superclass (Consumer0 and Producer0) and a subclass (Consumer2 and Producer2). Assume V extends U extends T.
Can you list the subclasses and superclasses of Consumer>?
Personally, I have to think carefully about this for a minute or so, and I've been there before a couple times.
This is not an especially complex scenario either. I've seen things get worse in practice.
Earlier quoted context omitted.
I'm not really sure what you mean. Variance just changes what is and is not a subtype/supertype. If List is covariant then List is a subtype of List if T is a subtype of U. So then, by induction, List > is a subtype of List > if T is a subtype of U. I'm not sure what's hilariously hard to follow here...
Good point, it's a bad example because we assume we're just combining covariance which is intuitive. Nevertheless, I have a point because: 1. Things get harder with contravariance. 2. Things get harder when you mix covariance and contravariance. The prototypical covariance class is Producer (with method produce() returning T) while for contravariance that's Consumer (with method consume taking a T as parameter). Assu…