Interesting timing as I had just read a great blog post about getting around the lack of generics in Go entitled "Living without generics in Go"[1] that I found to be very enlightening.
That was the way I used to write code when playing with Go, but in long term is necessary to write own wrappers for any tuple {collection, collection method, type}.
Interesting post, but what the author doesn't seem to realize is that even though he isn't doing much generic programming, someone needs to be able to do it to write the libraries he needs to use. The proposed approach is decent, but it reminds me of wrapping up STL container types in the amount of useless boilerplate needed to achieve a single function (but worse, because here the function is "type safety", whereas when you wrap an STL type it's about e.g. ensuring some invariant on the members of a set)
The author here. I'm not necessarily an opponent of generics, the post was just a "how to deal with the current situation", so keep that context in mind. :)
> someone needs to be able to do it to write the libraries he needs to use
I'm not sure what this has to do with what I wrote. As evidenced by the `container/list` package, a generic implementation can be distributed, and we can build our type-safe wrappers around that (if type-safety is really that important in your application--I haven't had much difficulty using the generic container as-is).
I do agree that the boilerplate is inconvenient (pretty much by definition). But it's fortunately also trivial by definition. People seem to think that these wrappers raise the total complexity of the program just because they contribute to the total lines of code (loc) count. Many people also think that if you don't use these type-safe wrappers that your program will be doomed to runtime errors.
I think both of these are big exaggerations, and that's why I wanted to write that post. Even in spite of Go's generics situation (I'm not entirely convinced about the degree to which it's a liability), I still find it to be the most enabling tool in my bucket.
>As with any feature it should solve a set of problems, if the problem isn’t solved in the best way possible by the feature then it’s either not a good feature or of very limited value.
This is disingenuous -- just to set an artificial barrier against generics.
Go doesn't solve any problem in the "best way possible":
- "Blessed" containers like Go's hash tables are not "the best way possible".
- The way it handles goroutines/channels has issues (mutability/pointer issues, not pre-emptive, etc). Again, far from "the best way possible" (that would be Erlang maybe).
- The GC was far from the "best way possible" or even "really good". Especially on the earlier versions (and crap on 32-bit).
- Losing type checks by having to use interface{} for generality, or resolving to BS textually generated code with custom string templating is not "the best way possible".
So why only Generics has to be "perfect or bust"?
And then there's this gem, about the (lack of) need for generic data structures:
>map/slice suffice for most of the cases encountered in programming.
...
Or this "con", about having generic to help with functional fold/zip/map:
>The fastest solution needs to take into account when and which order to apply those transformations, and how much data is generated at each step
So, in other cases the hit from interface{} is OK, but suddenly now those operations have to be "the fastest solution".
(Not to mention that the fastest solution, application order etc are orthogonal to Generics).
It seems to be a rite of passage that everyone who gets to a certain point of familiarity with Go has to write an article justifying why you don't need generics. Austerity can be a tough way to live until you become a bit numb.
Another rite of passage is the "logger" library, where every Go coder I know (including me) writes a "better" logging library because the standard one is so terse. A few thousand lines of code later, everyone quietly discards their overcomplex library and reverts back to the standard log.
The same goes for most of the standard library "improvements" that people make. Http middleware is necessary until you grok the power of Handlers, ORMs are prevalent until you get that it's easier to write specific CRUD routines for your structs using the standard lib.
I don't think it's austerity any more. I think it's just putting a high value on simplicity, and I really appreciate that now I understand it better.
I'm happy to let the generics argument rage and smarter people than me resolve it.
I don't need generics in my coding, happy to work around the occasional time I'd use them, but I understand the "argument from make" - if the Go team needed generics to build the language, then surely they're a Good Thing and we should have them.
I don't know. I program in C++ all day long--it took a loooong time to figure out the good patterns in the sea of options or to learn to interpret the cryptic and/or vague error messages (I just got an "invalid use of class" error today with GCC that contained nothing more than a line number for data). And that's not counting the sea of build systems, style guidelines, etc. And even with the generics situation, the amount of boilerplate in Go is a tiny fraction of that in C++.
I'm quite happy to yield control to sane defaults, widely-accepted conventions, and obvious idioms when I'm at home. If I ever need to write some high performance application, I'll definitely consider switching back to C++ (or perhaps learning Rust), but Go is great for rapid development of decently-fast-executing programs.
Then they should just own it and say "Go isn't getting generics ever." instead of making artificial barriers. Which their explanation totally is doing.
If that had been re-worded to "if the problem isn't solved in the most pragmatic way possible" then I would suggest that the sentiment would be a) more in keeping with the general philosophy of Go and b) a justification, sort of, for only supporting map and slice.
I think you're interpreting that sentence a bit too literally. Clearly the developers of Go are interested in very carefully balancing language features with language complexity (seemingly both for language users and language implementers). And it is in that sense that they probably mean solve in the best way possible, namely to solve a problem in the best way possible with those two concerns - complexity, "expressitivity" - carefully balanced[1].
[1] Though one can argue that the word "best" seems inappropriate when the concern is a very subjective balancing act.
Thanks, didn't intend for it to end up on HackerNews - at least not yet. It's still the first draft, most of it I wrote up in ~2hours. I mentioned on the forum that this document (currently) is highly biased towards my views, this is unfortunate, but this can only be fixed after numerous reviews.
Thanks for pointing out the problems with wording. I'm slowly fixing those. I've changed that "best" to "As with any feature it should solve a set of problems, if there are better ways of solving the problems, those should be used instead."... it's better but not perfect.
"in other cases the hit from interface{}" - added that addition to the alternatives section.
Generics don't have to be perfect, but it must be actually useful and it mustn't compromise simplicity of Go.
It doesn't mention the major inconsistencies in golang at all.
Go has generics. For arrays, slices, channels, maps, and some "standard" library functions. Go has generic functions. "append" being the obvious example. Go has polymorphic functions : append, make, ... If you look at the compiler source, the type system has generics, to a large extent. It's simply not accessible to normal programmers, instead only allowed for golang's authors.
As if this is not bad enough. Go cheats the type system in thoroughly non-obvious ways to get more C-like behaviours that obviously do not improve safety or programmer productivity.
Go has shudder return value polymorphic functions. Range being the obvious, but not only example (range changes in meaning depending on it's parameters AND on what you assign the result to).
As for your boxing argument. As soon as you use reflect, you're boxing everything. Oh wait, if you use a package that uses reflect, of course you're also boxing everything. Surely that can't be much ... hmmm half the standard library uses reflect. Fmt, everything in encoding, text or html templates, rpc, jsonrpc ... No need for boxing, or compile-time function evaluation or generics (all present better solutions to this, for the CTFE solution see D-lang).
Golang even has functions with type parameters. Make and new being the obvious examples. Again, this is present in the type system (not just there, also in reflect, things like printf, ...). It's simply not made accessible to us lowly go programmers.
So can we please stop with the crap that generics aren't necessary ? That nobody uses advanced type system features ? Or that polymorphism isn't necessary ? It's crap, and obviously the golang authors don't actually believe that. They just think that only they can be trusted to write such functions and that their datastructures are all anyone ever needs. If you disagree with that assessment (and everybody will, soon) Go is not for you.
So Go is simply a throwback to pre-80s programming languages, and a perfect illustration of the frustrations that led to the development and extension of the C++/Java/C# programming languages. If you give people the minimal implementation of sorting an array of structs, it becomes blatantly obvious why other languages are necessary.
The more I write Go, the more I yearn for C++. There is nothing in the C++ language that I can't do for my own datastructures/functions/... That is a huge feature, and may I just say :
From my cold, dead hands !
Btw : there is a better solution to generics as well at compiler-level, that is used in things like the CLR. First, generate unboxed versions of functions where that makes sense. If specializations are possible for 1-bit, 1-byte, 2-byte, 4-byte and 8-byte primitive data types, expand those. Then for anything bigger, like structs, create a boxed implementation, because for structs there is no longer any advantage to working unboxed. Then make it possible to pass in the version to be used in a parameter, so you don't have a combinatorial explosion when many type parameters are given.
> It doesn't mention the major inconsistencies in golang at all.
It has little relevance to "Generics". They are simply how Go is implemented, the question is whether "Generics" as a user implementable thing should be added.
> CLR
From my quick reasearch, it uses runtime code generation and Go must run in environments where that is disallowed.
It has relevance to generics. Right now the question implies Go does not have generics, which is not true at all. The question should be reworded :
Go has generic types and generic/polymorphic functions. Therefore all the focus on that such functions are not necessary/useful seems entirely beside the point, the result of misunderstanding. Can normal Go programmers please be allowed to make use of that feature ?
Btw : there is a better solution to generics as well at compiler-level, that is used in things like the CLR. First, generate unboxed versions of functions where that makes sense. If specializations are possible for 1-bit, 1-byte, 2-byte, 4-byte and 8-byte primitive data types, expand those. Then for anything bigger, like structs, create a boxed implementation, because for structs there is no longer any advantage to working unboxed. Then make it possible to pass in the version to be used in a parameter, so you don't have a combinatorial explosion when many type parameters are given.
That sounds sensible.
I'd like to figure out a way to extend the concept of interfaces to cover use cases like generic containers, but I'm not quite sure how that would work.
>* Thanks, didn't intend for it to end up on HackerNews - at least not yet. It's still the first draft, most of it I wrote up in ~2hours. I mentioned on the forum that this document (currently) is highly biased towards my views, this is unfortunate, but this can only be fixed after numerous reviews. Thanks for pointing out the problems with wording.*
Thanks for not being defensive and taking my rather harsh critique lightly!
I wasn't trying to attack your specific document either, it's just that those things have often been repeated (often verbatim) by the Go-dev camp.
Basically, I have nothing to be defensive about - it's a summary of discussions, which means that any properly stated argument should be incorporated to the document, no matter what I personally feel about that argument. Although, the content has to be moderated somehow, I dislike deciding what is "properly stated" argument, but I don't really have a better idea how to do it either, at least not yet.
Personally, I stand in the middle of the generics discussion so I pretty much can see both sides as a valid approach. And it's a WIP and I know if one person writes it, it will be biased no matter how hard you try.
PS: I find the "straight to the point" comments much more useful - they are much easier to understand. So don't worry about it :)
Trolling or not, comment is unproductive. Might I suggest offering alternatives such as rehashing the "rust is arguably a better design with similar goals" vs "rust's library isn't as complete and the language isn't stable" debate?
I have been writing Go code professionally nearly every day for 6 months. Would I rather be writing C? Probably. Python? Not on a project of this scale. C++? Toss up. Java? No - doing far to much interfacing to C libs for that. Rust? Probably. Go is far from perfect, but honestly not that bad.
I don't know much about Factor, it's a Forth implementation right? Just inspired by Forth?
Either way, the example[0] I was greeted with on the homepage[1] is quite awesome. Does it have an actor implementation like Erlang? How does it handle concurrency?
0:
USING: io kernel sequences
http.client xml xml.data xml.traversal ;
"http://factorcode.org" http-get nip string>xml
"a" deep-tags-named
[ "href" attr ] map
[ print ] each
It can be learned by a programmer knowing only imperative C-family languages in a couple of hours, meaning I can hire any average developer and make him productive in less than a week. Not the same as teaching pure functional style with pattern-matching and prolog's syntax. Go is also much more efficient. It also produces static executables and can be easier to deploy -- or not, depending on the exact situation. It handles strings nicely -- anything other than Erlang's approach to strings is nice.
Erlang has other advantages, though. 99.9999999% uptime and hot code replacement are easier with it, for instance. Their ideal niches don't completely overlap.
Comparing Rust to Go is equally unproductive, given that they target wildly different use cases and embody dramatically different philosophies. You might as well compare Lua to Objective-C, or OCaml to Malebolge.
It is. The Go language devs should just own it and say "No". Will the arguments shift somewhere else? Maybe but maybe the "Go needs generics" folks will just go away.
>The Go language devs should just own it and say "No"
Which is the grown up, honest, thing to do.
But I'm not sure about the other thing you mention:
"Maybe but maybe the "Go needs generics" folks will just go away."
Why would they go away?
They ask for Generics because they have found some use for Go (say, because it has a OK standard library, or decent tooling and momentum compared to alternatives), but also found a need this feature.
Neither the "use of Go" they found, nor their need for generics will disappear if the language devs say "no". If anything, as stakeholders in the languge (a user is a stakeholder) they'd be even more pressed to change the language dev's mind -- down to the fork point.
How many years to go before go users/developers outside of google will fork "go lang" the way node.js users/developers did ?
the go-lang team at google is very smart. It's not that they have a huge amount of high-priority work other than generics. Why don't they address a summary of the sentiment on hacker news on generics ?
If you're a programmer, your life is made easier if you have access to generics.
If you're a language developer, your life is made harder if you have to add generics.
And if your language gets all the people that are easily satisfied with something "static and faster to write than C" why go the extra mile?
Especially if your goal is not building a commercially succesful language, or an advanced language, or a language to solve 2014's problems, but just to hack on what you have created.
Besides, being a great programmer and a great language designer are two different skills. Would a language created by Linus be that good?
>Once they do, you know not every "generics or bust" will be satisfied and the anti-Go hate will simply shift.
No, I don't know that.
Do you suggest they are lying and just hate go out of pure irrational emotion? (Despite some of the critics having writen thousands of lines in Go, or some like pjmlp have even had code in the standard libs).
Is there evidence to suggest that?
Does that hold true for other languages too? When people criticize a specific problem a language/technology has (e.g. Python's GIL or PHP's lack of proper language implementation with AST et al, SOAP for its verbosity, etc), is that out of "haters gonna hate" mentality?
Does that extend to that programmers cannot recognize constant pain points in a language, and those that complain about such things are irrational?
I find the idea expressed in the comment incoherent and illogical itself.
It's quite true, in fact. Look at Java's generics. Pre-Java 5, everybody was asking for generics. They got implemented. Now everybody complains they suck because they are a hack, they are slow, not that type-safe, made the language more complex, are not as powerful as C++/Ada/Haskell, etc. Nobody wants to go back to Java 4 (in which situation was worse than Go, tbh), but everybody (OK, most people) keeps complaining.
I'd myself be very happy with a hint of generics in Go, though. Something at the package level, that would be instantiated at "import" time (since the import keyword is supposed to hold some magic in itself) would have my preference.
There is only two ways to compile generics C++ (instantiation) and Java (type erasure) style. [0]
Alternatives to these are just manual or semi-manual variants. For example, Go people seem to like the "empty interface" approach. Well, that is Java-style type-erasure done manually. The code generation approach is just C++-style instantiation done outside of the compiler.
So, C# does instantiation, but is usually clever about reference types.
I am not quite sure from the interview what happens about constraint types. It seems the reference is cast into an Interface type (IComparable) and used as such. In other words, boxing.
The arguments used for contrained type parameters can be checked by the compiler, so no casting is necessary at runtime
"... C# does strong type checking when you compile the generic type. For an unconstrained type parameter, like List<T>, the only methods available on values of type T are those that are found on type Object, because those are the only methods we can generally guarantee will exist. So in C# generics, we guarantee that any operation you do on a type parameter will succeed."
Edit (more explicit quote):
"When you say K must implement IComparable, a couple of things happen. On any value of type K, you can now directly access the interface methods without a cast, because semantically in the program it's guaranteed that it will implement that interface. Whenever you try and create an instantiation of that type, the compiler will check that any type you give as the K argument implements IComparable, or else you get a compile time error."
Furthermore (related to the CLR avoiding boxing costs):
"I'm just pointing out that we do fairly aggressive code sharing where it makes sense, but we are also very conscious about not sharing where you want the performance. Typically with value types, you really do care that List<int> is int. You don't want them to be boxed as Objects. Boxing value types is one way we could share, but boy it would be an expensive way."
What I wonder is: Take "SortedList<T> where T:IComparable". Now there is a generic add(T t) method, which needs to call t.compareTo(x). As add does not know the dynamic type of t, we don't know the vtable offset of compareTo for compilation. Thus the compareTo call cannot be compiled to "load method pointer from vtable; call it". We need something more expensive or JIT magic (traces,guards,etc).
What I wonder is: Take "SortedList<T> where T:IComparable". Now there is a generic add(T t) method, which needs to call t.compareTo(x). As add does not know the dynamic type of t, we don't know the vtable offset of compareTo for compilation. Thus the compareTo call cannot be compiled to "load method pointer from vtable; call it". We need something more expensive or JIT magic (traces,guards,etc).
The .NET generics are also interesting to contrast with C++ and Java since they are implemented in a virtual machine that allows interoperation between different languages. Not really applicable to the Go discussion, though.
If they give us type safe c-like macros I will happily live without generics. This is from a Scala dev. I write Go because it's easier and less error prone than going through JNI. However the lack of any reasonable abstractions in Go makes it my last choice language.
What I find frustrating is that they seem to think that there are only two solutions - C++ (create a new type for each variant) or Java (erase the type information).
> What I find frustrating is that they seem to think that there are only two solutions - C++ (create a new type for each variant) or Java (erase the type information).
That, at least, is correct. Generics can be erased (compile-time only) or reified (generic type instances exist at runtime).
C# uses reified generics, and Mono creates (created?) a reification for each parameter typeset, Microsoft's implementation creates the same underlying reification for all reference types as a space optimisation, but it still uses reified generics.
There is a good helping of dishonesty in using C++ as the "reified generics" example though, as C++'s templates do significantly more than just reify generics (template specialisation, code generation, etc…) and that added to the interactions between templates and other language features makes them significantly more complex than e.g. C#'s, Rust's, Ceylon's, Kotlin's, and most other instances of reified generics.
Isn't the point of the article that Go doesn't actually beat Algol 68?
> The answer is that they're both very, very similar, with much the same level of functionality. They overlap more than they differ.
> Go has more in the way of actual features --- interfaces, channels, maps, defer , fallthough , proper closures --- but Brand X is more consistent with its features. […]
> Go, on the other hand, is full of corner cases. […]
> Most killingly of all, it's full of examples where they did not consult the literature. […]
> Brand X doesn't have any of these issues. Oh, it's got issues all of its own --- for example, the oddities in the reference type system; no varargs functions; no proper object system; the lack of little bits of syntactic sugar like keyworded composite structure literals... but it feels complete in a way that Go doesn't. I can use Brand X to implement nearly all the features of Go. I can't use Go to implement the features of Brand X.
> The only major features that Go has that Brand X doesn't are proper closures, and the interface system. And I'm afraid that's not enough.
This seems to come up at every discussion about Go. What usually also comes up is how Rust is a much better designed language which solves real problems, while Go merely improves a little over C while leaving most of the same death-traps lying around.
Yet at the same time Go seems to have a much bigger adaptation than Rust. What's up with that? Are Googlers paid to create projects in Go, or are people genuinely happy to have anything which sucks less than C and stamped with a "final" badge of approval?
Go is older, so it has had more time to gain users. And go has gained users mainly from python and ruby, not from other compiled languages. People are genuinely happy to have anything which sucks less than scripting languages but still offers comparable development speed. That's where go is doing well.
>Yet at the same time Go seems to have a much bigger adaptation than Rust. What's up with that?
Well it has to do with Rust not even being 1.0 yet, whereas Go has been stable for 5 years...
Or with Rust changing some syntax in every point release for most of it's design cycle, so even early adopters had a difficult time.
Or maybe with Go having Google behind it, with the increased visibility it entails... (Rust, for one, never had huge Google Developer convention sessions exposing it to thousands of devs -- or any conventions for that matter).
And yet, we're already seen increased adoption for this pre-1.0 rust, major community playahs like Katz getting in the core team, more posts and libs every day, etc...
Being stable for five years rather than still being under development can be seen as a feature. It is a strong selling point of Go, actually : solving actual problems rather than making a perfect language. It's a perfect example of Stroustrup's quote about languages everybody hates vs languages nobody uses.
And again, people compare Go and Rust while they have completely different goals.
Go is about capturing dynamic languages' flow of development and put that on top of types and static compilation so you can improve early on performance and correctness (That doesn't even include inserting a "generic" way of coding concurrent processes).
Rust is much more about replacing C++ in features (from my little-knowledge POV): provide higher-level constructs to model your problem than raw C, with a heavy control over memory for maximum correctness and performance.
Rust may be better designed, yet I find it much more verbose and harder to understand than Go. While this may be a reason for the difference of adoption, a much bigger reason must be the difference in maturity.
>Go is about capturing dynamic languages' flow of development
The only features it has regarding that is GC, built-in hash-table and type inference. Hardly the gist of what makes dynamic languages flow a fast/nice one.
Plus Go was also advertised as "systems language" -- and besides, the same kind of programs Go is used for, servers, REST, network infrastructure stuff, etc, Rust will be used for too.
It's not like Rust is only (or primarily) for anything much lower level than Go.
> The only features it has regarding that is GC, built-in hash-table and type inference.
No, the real feature is understanding that a language doesn't stop at the syntax, the tooling and the environment are extremely important:
- unless you want to build multiple binaries in one command, there is no need for any build helper.
- hack-compile-test cycles are really short, to the point you can run simplest go programs as scripts
- go has its own package manager, allowing you to manage dependencies extremely easily (not technically specific to dynamic languages, but in practice often associated with them) (Note that I'd prefer if everything was managed by my OS package manager, but we're not there yet)
The go creators (and community) has spent a lot of effort in improving the overall development environment, and this is a characteristic I have seen more developed in dynamic languages (again, I'm not saying that it's tied to a type of language, but I have seen this more developed with the pythubyerlascripts)
> It's not like Rust is only (or primarily) for anything much lower level than Go.
It's not, It's just targeted at different problems. You wouldn't use Go to build a game engine [1] (you need tight control over memory and have some realtime requirements), and I'm not sure you need the tight control that Rust gives you over memory when you build an image processing server [2] (you'll need concurrency primitives for this)
Rust is a much better designed language which solves real problems
Yet remarkably people are solving real problems with Go. Despite all of these dire predictions and pontifications on here by so many mortally offended by Go's existence, people are simply making solutions and moving on.
I don't believe I've heard of a single significant solution with Rust. I mean, I'm sure one exists somewhere, but it seems that it's caught in the advocacy trap of people talking about how great it is (overwhelmingly in Go discussions, it seems), instead of building solutions with it.
I don't know what people keep comparing Rust and Go. It might be that they are both after the coveted replace C++ spot shrug.
That said, Rust doesn't have any real applications, because it's not out yet. The core team pretty much states, don't build anything significant in Rust atm.
It'll probably be out next year. Also Rust is a much more difficult language to grok, because it forces you to care about borrows.
Rust does have two major production deployments (OpenDNS and Skylight) and we've heard rumors of a few more. (Just this the other day: https://news.ycombinator.com/item?id=8743015 )
That said, yes, before 1.0, you have to be a special kind of person to do such a risky thing. That said, Skylight can't really do it another way.
>Yet remarkably people are solving real problems with Go. Despite all of these dire predictions and pontifications on here by so many mortally offended by Go's existence, people are simply making solutions and moving on.
Also people do that with C, C++, Python, etc etc, so no need to invent Go at all, right?
In addition to the points already brought up by sibling comments: Go is centred around automatic memory management. The downside to that is that Go might not ever be suited for certain domains: the upside is that automatic memory management is good enough for arguably most domains. Now, consider that many Go developers are coming from scripting languages; using Go gives them a lot of the performance boost, without also having to worry about the lifetime of their memory. And if they were using Python beforehand, chances are that implementing everything in C/C++/Rust instead won't really be necessary for them. Go gives good enough performance.
This overall comparison might be a dead end, though. After all, we're comparing languages that effectively only have in common that they seem to have been spawned at vaguely the same time. Their philosophies are totally different.
> Rust is a much better designed language which solves real problems
Well, so far it looks like Rust resolves real problems in theory. In practice, I just see much more projects made in Go than in Rust. I confess that I didn't go hunting for Rust projects though.
Maybe it's the same as Mercurial vs Git. The latter is more popular, even if it's "worse", according to some.
The comparison is unwarranted. Mercurial and Git were designed to solve the same problem (quite literally, even: both were conceived as an attempt to replace BitKeeper as the distributed version control system for the Linux kernel). Go and Rust were designed to solve very different problems. That people feel the need to constantly compare the two languages is because our industry is fad-driven, causing people to be ever on the lookout for The Next Big Thing lest they get left in the dust. This incessant veneration of novelty results in people who have only shallow understanding of a large number of technologies, leading to comparisons that are based only on superficial traits.
Reminds me of Hoare's quote on ALGOL 60: "Here is a language so far ahead of its time that it was not only an improvement on its predecessors but also on nearly all its successors."
yes absolutely agree. Go is better than plain C, but people aren't using it for things C is used for which I think makes a big difference.
When it comes to developing software applications, the really weak type system of Go is frankly just causing developer pain. IMHO, generics are the missing feature in this language, just try writing a generic map or reduce function in Go and the amount of code you have to duplicate is ridiculous. Interfaces and using relection is not safe and annoying and having all the XXXInt, XXXFloat, XXXMyType is crazyness too.
For what its worth, my dream language would be Julia's type system grafted onto Go.
This caught my eye because I recall you being someone who has vigorously criticized Go in every single Go discussion. A quick search and I can find lengthy critiques from you going back four years.
Given that he has been talking about how much Go sucks for almost the entirety of its existence, I highly doubt they've ever developed in Go. It became something to be an advocate against, not a tool in the belt.
I moved on in the sense that I am not arguing about it any longer.
If you read correctly my post, I just said I would like to see the discussion about generics take into account what other generic systems besides Java and C++ offer.
In those four years as you say, I have never seen such a document, even most discussions on gonuts went around Java, C++ and .NET implementations.
There are mentions that a prototype was made, yet the conclusions haven't been shared with the outside world.
If the designers don't want to implement generics, I can always look to Go as Active Oberon with C like syntax.
I see more value arguing about using Go for user space applications that are still made in C, just because the authors needed a compiled language than arguing about the essence of generics in Go. That boat has long left.
Edit: Just got to read Ian's opinion, quite interesting.
> my dream language would be Julia's type system grafted onto Go
With Julia's type system you get a really good parametric multiple dispatch, which is a completely different (and more satisfying, to me) way to organize code than in Go. Go wouldn't be Go at all with Julia's type system.
If that's what you want, why not just program in Julia?
Casting to and from void pointers is very much not generics, and is in fact exactly what you have to do in Go (casting to and from `interface {}`) or pre-generics Java (casting to and from `Object`).
Although at least in Go and Java the downcast is checked and memory-safe, not so in C.
I was responding to a comment claiming that Go is better than C in discussion about generic programming. I pointed out that C in fact supports generic programming (actually by void pointers and function pointers). Why you (and other downvoters) respond with the way to do it in Go isn't quite clear to me. I didn't claim anything about Go. I claimed something about C.
Maybe because Go's approach, although lacking, is still better than C in terms of type safety.
When you cast from void* to whatever type, the compiler will happily accept it and bad things will happen if the type is wrong. Maybe not right away, thus the program will bleed with corrupt memory.
With Go when you cast to the wrong type, a cast violation will occur and the program will panic right away.
I think that's memory-safety rather than type-safety: in Go (or Java or C#) the types may not be correct (so it's not type-safe) but it'll fault at runtime rather than possibly corrupt memory as C would.
But without the speed of C. D and Rust are both better than C and better than go, while still being as fast as C (ok rust isn't always as fast as C yet, but it is still faster than go).
It is partly a compiler issue, but the semantics of a language limits how fast of a compiler you can make. It doesn't really matter though, the fastest g compiler still produces slower binaries than any C compiler I can find.
> do you want slow programmers, slow compilers and bloated binaries, or slow execution times?
Easy I want "slow compilers and bloated binaries".
In my experience "bloated binaries" in the real world aren't as bad as one would think, since instruction cache problems only appear when your working set is huge, not when your total binary size is huge.
Equating big binaries with slowness is simply misleading. What's next, increased download times?
> The generic structures are less useful than a concrete one.
Without generics you can't separate algorithms from data structures so everything is much less useful. If the members names are worse, sub-type then rename symbols.
> Generic structures can be less optimized than a concrete solution.
False. That "concrete solution" not only has to be written, but it will also perform identically to an instantiated template.
And type erasure is not a solution, it prevents inlining, constant folding and hoisting.
Eg: In C++ std::vector<MyObject>::operator[] has 1 indirection, while using a generic C void* container would have 2. Guess which one is faster?
> Generic algorithms can be less optimized than a concrete solution.
sigh
Also wrong since generics do not prevent specialization if needed. See: static if in D, template specialization in C++.
> In my experience "bloated binaries" in the real world aren't as bad as one would think, since instruction cache problems only appear when your working set is huge, not when your total binary size is huge.
The "bloated binaries" part is especially odd given the state of the Go compiler's optimisations (or lack thereof):
$ cat >| test.go
package main
func main() {}
^C
$ go build test.go
$ wc -c test
467008 test
$ cat >| test.go
package main
import "fmt"
func main() { fmt.Println("Hello, World!"); }
^C
$ go build test.go
$ wc -c test
1806192 test
> Without generics you can't separate algorithms from data structures so everything is much less useful.
Generics is not the only way to separate data and algorithms. I agree that it is possible to sub-type/embed and rename - although looking at generic code, I rarely see that happen.
> That "concrete solution" not only has to be written, but it will also perform identically to an instantiated template.
When you know where exactly the code will be run, it can be possible to create a better package taking into account the context where it will be run. By concrete, I mean a package in a real-world problem.
Basically, abstract things are harder to optimize for a particular case.
> Generics is not the only way to separate data and algorithms.
Do you see any way other than the 3 ones discussed in the OP article under the label "Generics"?
> When you know where exactly the code will be run, it can be possible to create a better package taking into account the context where it will be run. By concrete, I mean a package in a real-world problem. Basically, abstract things are harder to optimize for a particular case.
I could agree with that if time wasn't limited, but imho building custom data structure/algorithms for each problem will precisely leave less time to optimize for performance since reaching a working state first will be longer without reusable abstractions.
> Do you see any way other than the 3 ones discussed in the OP article under the label "Generics"?
The one I mainly meant was interfaces. Of course there are also DSLs and code generation.
> I could agree with that if time wasn't limited, but imho building custom data structure/algorithms for each problem will precisely leave less time to optimize for performance since reaching a working state first will be longer without reusable abstractions.
I mentioned that in the summary. It's about tradeoffs. Even if all the cons/pros are listed people could still come to different conclusions, because people value different things. And there's nothing wrong about it.
The amount you need to reimplement depends on the amount of available third-party packages - the more good packages there are the less you need to build those custom data structures.
Let's just use the C++ approach. Maybe with more restrictions to prevent abuse. Maybe not. To me the problems with C++ templates arise from their verbosity (Go's type system is rigid, but defining types is easy and encouraged and the compiler does inference for variable declarations), their abuse (let's keep it to simple type insertion), and the nasty errors / debugging (Go's type system rigidity should also help here).
Just being able to do very light type insertion and the transparent code generation to go along with it would go a long way.
I was considering Go for some project of mine but the lack of generics turned me away because the project needed to do some heavy numeric computations over custom containers (sparse matrices).
Now days I think that adding generics to Go will be disaster - Go lacks sound type system and piling generics on top of it will make the thinks even worse.
I find it amusing how angry people get about this, like really, vehemently angry.
If this issue is so important to people, don't use Go, use C++, Java, D or Rust. Don't waste your life arguing about something you'll never use, it's not a productive use of your time! Think about how much time you could save!
Alternatively, propose a solution to the problem and try and gain some traction to get it implemented into the language, or fork Go and implement it yourself. There's evidently a hunger out there for generics in Go, but everyone keeps assuming the responsibility lies with the Go Core team who's priorities seem (right now) to be in the optimising GC/compiler area.
This is exactly it. There are a lot of people who have argued passionately about this (the same crew of commentators appear with the same complaints in every single Go discussion), and now they are so deep in their advocacy that they feel their pride depends upon aggressively and vigorously defending it. It has gotten worse every time this has come up, and most of the comments are the logical conclusion. I mean seriously one of the comments calls it "ghettolang" without parody. Is this really Hacker News?
And exactly as you said -- for those people so offended by this: Move on. Use something different. Go isn't for you. As someone who has integrated Go very effectively into my life, I'm rather glad the Go team isn't listening to every railing fanatic and their pet issues.
Nothing ironic about it. There are very vocal, very passionate people who pile the hate on Go in every single discussion regarding it. The list can usually be summarized as "ways that Go is not Haskell".
Which is strange, because a post about Haskell tends to be about Haskell. A post about Go tends to be a lot of people telling everyone how Go is unlike, to its detriment, other languages. Great -- go use those languages.
Why people assume that only people NOT using Go are complaining
HN doesn't have a large community of commentators, and you get to know the common personalities. There are absolutely people building solutions in Go who might think "it would be nice to have generics", and indeed that's exactly why they've never been discounted or denied by the Go team, simply deferred. But these discussions are dominated not by those people, but instead people telling you why Go is not Rust is not Haskell is not C# is not...
What's the point? We have a lot of languages and environments to choose from, and we do. Why are people seemingly threatened by Go? It's bizarre.
Generics are nice and they're important, but there are a lot of nice and important attributes to a language, and they have bigger priorities. If generics were critical to my work, I simply wouldn't use Go (and indeed, for some subsections and components, I don't for that reason. Life goes on. Different tools serve different needs).
I don't think anyone's threatened by Go. I think some people are disappointed that Go's designers chose not to add a lot of extra type-related features, including generics, for some extra effort on the part of the language designers but very little cost (and much benefit) to the language users. If this was an unused toy language there would be no problem, but the existence of lots of code written in any language will have a small cost on other developers for social reasons, even if they don't use that language.
Yet your final sentence says exactly the opposite, indirectly. As if Go is some sort of scourge that these gallant guardians of language must fight.
Further, if you assess the simplicity of adding generics, and it is at odds with the actual people creating the language and tools (among whom are some of the most respected names in this industry), that's a point when you should stop and reassess your own, outsider-looking-in perspective.
The appeals to authority are not always used in a fallacious way (like the informal logical fallacy). But I don't personally like it, and I think it is intellectually lazy. It's better to let the product itself stand or fall on its own merit.
Your incantation of the logical fallacy is how it often appears in what could best be described as ignorant conversations.
The appeal to authority fallacy is if I measured the acidity in a lake over a long term period, and then presented my findings and someone declares that such can't be the case because the Environment Agency says it's fine. They're undermining my objective, demonstrated efforts with a hand waving appeal to someone else. If, on the other hand, I casually and lazily said "I think that there lake is acidic, no matter what anyone tells me", it isn't an appeal to authority to point out what someone who is undoubtedly in a better position thinks and actually knows.
In this case someone offhandedly, based upon nothing but the fact that they seem to want it to be true, declared the ease with which the Go team -- you know, the actual people working on it -- could add generics. No fuss, no muss. It isn't an appeal to authority to point out that they are in absolutely no position to make such a claim.
> Your incantation of the logical fallacy is how it often appears in what could best be described as ignorant conversations.
Like I said; I didn't mean that you, or necessarily people in general, use the appeal in a fallacious way. But it was my fault for not making that clear to begin with.
This is a general note, not thinking of any post or argument in particular: I can hardly count the number of times people who advocate Go will bring up the fact that people like Rob Pike and Ken Thompson are or have been involved in the project, and how smart they are and even 'how can I even question their work, given their skill and talent'.
>Which is strange, because a post about Haskell tends to be about Haskell. A post about Go tends to be a lot of people telling everyone how Go is unlike, to its detriment, other languages. Great -- go use those languages.
What you write above shows that HN users don't have the same behavior with other languages indiscriminately...
So, doesn't this sound like maybe it's Go that's problematic and not HN user behavior? (see also below)
>There are absolutely people building solutions in Go who might think "it would be nice to have generics", and indeed that's exactly why they've never been discounted or denied by the Go team, simply deferred. But these discussions are dominated not by those people, but instead people telling you why Go is not Rust is not Haskell is not C# is not...
And why shouldn't they? (Well we)
Go has design problems (beside the lack of Generics) the same way PHP has or JS has.
And it's criticized in a similar manner as when JS is brought up its type coercion is mentioned, or its global by default scope, its confusing this context, etc.
Now you might agree with the above or not. But what you ask is essentially for people that perceive it that way, to not speak, and just let it go.
Isn't the whole point of HN discussion (including about languages), criticism etc? It sure is not about celebrating all languages just because they exist...
I was thinking this exactly. As an external observer (I don't use go, don't intend to. I do look to it as an interesting example language though) all the vitriol is very puzzling.
And you're right, the Go core team does not seem interested in adding generics anytime soon.
Personally, and this is my opinion, I find myself wondering if old school hackers like Thompson, Pike, and Fitzpatrick (and others) find themselves not needing generics, or that slice/maps are enough, maybe I should ponder that for a while. Ie do they know something I don't? Edit; or maybe the type of software they're creating with go is different than I as a generics user, am creating?
So should people stop talking about the pros/cons of programming languages at all? Because this can also go the other way. Another person can say: "If you enjoy programming in Go, then use it but keep quiet and stop promoting it and talking about how much you like it."
I haven't used Go at all yet, but I'm interested in hearing more about it the good and bad parts, along with other languages. Telling others to not complain at all doesn't do any favors.
Remarkably there has been close to zero promotion of Go on Hacker News. Occasionally various incremental update things appear on the front page, just as there are updates on C# 5, the next version of Java, etc, but there have been shockingly few "Why you should use Go" type posts.
People just use it. They don't advocate it. But when it comes up, inevitably it becomes an advocacy starting point for alternative languages. Somehow every single Go discussion, even about the most banal thing, ends up being about Rust, Haskell, F#, and so on.
Koolaid point. Go is a much, much worse language than e.g. D, Rust, OCaml, but somehow it gets all this attention. I appreciate it's not productive, but you can see why this naturally makes people angry.
Perhaps the reason for anger is because of Go ignoring years of research and innovation. I would imagine people who were early adopters of power tools felt the same way.
Imagine if you were used to using power tools, but all the jobs available forced you to use hammers, screwdrivers, etc. Wouldn't this make you a little angry or at the very least disappointed?
Sorry, but I can't see the value of posting this to hacker news. As evidenced by many people in this thread assuming it's from one of the core Go development team, it's not.
Exactly. This is one person's attempt to distill the previous conversations down to one document. It's not supposed to be justifying or rationalizing anything, nor does it speak for the core language contributors!
It's one person's summary of a very long and convoluted conversation. That's all.
Didn't intend for it to end up on HackerNews - at least not yet. It's still the first draft, most of it I wrote up in ~2hours. I made a mistake for not putting the "Draft" note at the top of the page in the first page.
I try my best to be objective - obviously this isn't easy. This is intended to be a review of both sides of the arguments. The generics discussion comes up again and again - I made this document to stop arguments going around in circles.
I will go over all the suggestions and add any missing information. Of course, if you have some good overview of some particular generics implementation you can PM me, or simply add that as a comment.
1. Any old-timer Windows developer will tell you that statically generated code still has to be understood and maintained.
2. generics are about type-safety, not codegen. Codegen can be added on top of parametric polymorphism (e.g. C++ template specialisation), but is not required by any means (C# or Rust don't do any generic-based codegen beyond reification)
To be fair I've not worked much in a languages that support generics but I assure you there are type safe solutions to most every problem without them (as long as you don't write yourself into a corner). Whether my life could be simpler using them is unseen but to say the lack of them cripples a language is wholly untrue. You're simply used to using them in your solution and are wired to think with them. That's a flaw in the developer, a lack of imagination, not a flaw in the language. Arguing every language needs every feature every other language has is stupid. "[lang1] is bad because it isn't [lang2]" Is always an infuriatingly stupid and pointless argument. Use lang2 if you love it so much. Seriously.
That we can manually hack around this with busywork and tiptoe around the issue with various hacks doesn't mean it's not a flaw in the language.
It's 2014. Generics are a settled deal in programming languages. It's not some syntactic sugar or something the value of which is stull undecided and its advocates might "lack an imagination".
>Use lang2 if you love it so much. Seriously.
Well, some do. That doesn't mean they don't get to complain about something they perceive broken in Go that could be better. Especially if they could use Go (to take advantage of other benefits it might have) if it weren't for those shortcomings.
In general, the statement is analogous to "if you have a problem with X in america, why don't you immigrate", which is silly. If you have a problem, you criticize, and you try to change that. You don't just dismiss everything that's also not problematic.
The cons subsections that say that a generic version can be less optimized than a concrete version should mention which implementation is considered. If you use mono-morphization, then you end up with the same code as you would have written by hand, so there is no loss of performance there.
Abstract things are harder to optimize than concrete things. A package for a concrete problem can be more optimized than a generic/abstract package.
I don't mean that the manually specialized version is faster than automatically specialized template. Rather that this encourages to not write abstract data-types, but rather concrete types that solve a real world problem - editor.Buffer vs. gap.Buffer. Once you have a real world problem it can optimized much better for that use case.
As a language, I like Go. I would like it to have generics (Ada-style generic syntax might fit best). I don't care enough to get emotional about it. So, meh.
Also: does auto-boxing even make sense in Go? Between the fact that Go is statically compiled and that there isn't an int / java.lang.Integer dichotomy to resolve, I would assume that the compiler could just do whatever it needed to with primitives, regardless of generics.
Yes, and already happens: interfaces cause wrapping of their content[0] and go does basic escape analysis and automatically allocates things which escape a function on the heap rather than the stack[1]
148 comments
[ 3.5 ms ] story [ 219 ms ] thread[1] http://www.weberc2.com/posts/2014/12/12/living-without-gener...
> someone needs to be able to do it to write the libraries he needs to use
I'm not sure what this has to do with what I wrote. As evidenced by the `container/list` package, a generic implementation can be distributed, and we can build our type-safe wrappers around that (if type-safety is really that important in your application--I haven't had much difficulty using the generic container as-is).
I do agree that the boilerplate is inconvenient (pretty much by definition). But it's fortunately also trivial by definition. People seem to think that these wrappers raise the total complexity of the program just because they contribute to the total lines of code (loc) count. Many people also think that if you don't use these type-safe wrappers that your program will be doomed to runtime errors.
I think both of these are big exaggerations, and that's why I wanted to write that post. Even in spite of Go's generics situation (I'm not entirely convinced about the degree to which it's a liability), I still find it to be the most enabling tool in my bucket.
This is disingenuous -- just to set an artificial barrier against generics.
Go doesn't solve any problem in the "best way possible":
- "Blessed" containers like Go's hash tables are not "the best way possible".
- The way it handles goroutines/channels has issues (mutability/pointer issues, not pre-emptive, etc). Again, far from "the best way possible" (that would be Erlang maybe).
- The GC was far from the "best way possible" or even "really good". Especially on the earlier versions (and crap on 32-bit).
- Losing type checks by having to use interface{} for generality, or resolving to BS textually generated code with custom string templating is not "the best way possible".
So why only Generics has to be "perfect or bust"?
And then there's this gem, about the (lack of) need for generic data structures:
>map/slice suffice for most of the cases encountered in programming.
...
Or this "con", about having generic to help with functional fold/zip/map:
>The fastest solution needs to take into account when and which order to apply those transformations, and how much data is generated at each step
So, in other cases the hit from interface{} is OK, but suddenly now those operations have to be "the fastest solution".
(Not to mention that the fastest solution, application order etc are orthogonal to Generics).
Ideology, and a push-back against outside pressure to include them.
The same goes for most of the standard library "improvements" that people make. Http middleware is necessary until you grok the power of Handlers, ORMs are prevalent until you get that it's easier to write specific CRUD routines for your structs using the standard lib.
I don't think it's austerity any more. I think it's just putting a high value on simplicity, and I really appreciate that now I understand it better.
I'm happy to let the generics argument rage and smarter people than me resolve it. I don't need generics in my coding, happy to work around the occasional time I'd use them, but I understand the "argument from make" - if the Go team needed generics to build the language, then surely they're a Good Thing and we should have them.
I'm quite happy to yield control to sane defaults, widely-accepted conventions, and obvious idioms when I'm at home. If I ever need to write some high performance application, I'll definitely consider switching back to C++ (or perhaps learning Rust), but Go is great for rapid development of decently-fast-executing programs.
[1] Though one can argue that the word "best" seems inappropriate when the concern is a very subjective balancing act.
Thanks for pointing out the problems with wording. I'm slowly fixing those. I've changed that "best" to "As with any feature it should solve a set of problems, if there are better ways of solving the problems, those should be used instead."... it's better but not perfect.
"in other cases the hit from interface{}" - added that addition to the alternatives section.
Generics don't have to be perfect, but it must be actually useful and it mustn't compromise simplicity of Go.
Go has generics. For arrays, slices, channels, maps, and some "standard" library functions. Go has generic functions. "append" being the obvious example. Go has polymorphic functions : append, make, ... If you look at the compiler source, the type system has generics, to a large extent. It's simply not accessible to normal programmers, instead only allowed for golang's authors.
As if this is not bad enough. Go cheats the type system in thoroughly non-obvious ways to get more C-like behaviours that obviously do not improve safety or programmer productivity.
Go has shudder return value polymorphic functions. Range being the obvious, but not only example (range changes in meaning depending on it's parameters AND on what you assign the result to).
As for your boxing argument. As soon as you use reflect, you're boxing everything. Oh wait, if you use a package that uses reflect, of course you're also boxing everything. Surely that can't be much ... hmmm half the standard library uses reflect. Fmt, everything in encoding, text or html templates, rpc, jsonrpc ... No need for boxing, or compile-time function evaluation or generics (all present better solutions to this, for the CTFE solution see D-lang).
Golang even has functions with type parameters. Make and new being the obvious examples. Again, this is present in the type system (not just there, also in reflect, things like printf, ...). It's simply not made accessible to us lowly go programmers.
So can we please stop with the crap that generics aren't necessary ? That nobody uses advanced type system features ? Or that polymorphism isn't necessary ? It's crap, and obviously the golang authors don't actually believe that. They just think that only they can be trusted to write such functions and that their datastructures are all anyone ever needs. If you disagree with that assessment (and everybody will, soon) Go is not for you.
So Go is simply a throwback to pre-80s programming languages, and a perfect illustration of the frustrations that led to the development and extension of the C++/Java/C# programming languages. If you give people the minimal implementation of sorting an array of structs, it becomes blatantly obvious why other languages are necessary.
The more I write Go, the more I yearn for C++. There is nothing in the C++ language that I can't do for my own datastructures/functions/... That is a huge feature, and may I just say :
From my cold, dead hands !
Btw : there is a better solution to generics as well at compiler-level, that is used in things like the CLR. First, generate unboxed versions of functions where that makes sense. If specializations are possible for 1-bit, 1-byte, 2-byte, 4-byte and 8-byte primitive data types, expand those. Then for anything bigger, like structs, create a boxed implementation, because for structs there is no longer any advantage to working unboxed. Then make it possible to pass in the version to be used in a parameter, so you don't have a combinatorial explosion when many type parameters are given.
e.g. http://research.microsoft.com/pubs/64031/designandimplementa...
It has little relevance to "Generics". They are simply how Go is implemented, the question is whether "Generics" as a user implementable thing should be added.
> CLR
From my quick reasearch, it uses runtime code generation and Go must run in environments where that is disallowed.
How the generic code generation differs from the JIT approach I don't know.
Go has generic types and generic/polymorphic functions. Therefore all the focus on that such functions are not necessary/useful seems entirely beside the point, the result of misunderstanding. Can normal Go programmers please be allowed to make use of that feature ?
That sounds sensible.
I'd like to figure out a way to extend the concept of interfaces to cover use cases like generic containers, but I'm not quite sure how that would work.
[1] http://msdn.microsoft.com/en-us/library/f4a6ta2h.aspx
Thanks for not being defensive and taking my rather harsh critique lightly!
I wasn't trying to attack your specific document either, it's just that those things have often been repeated (often verbatim) by the Go-dev camp.
Personally, I stand in the middle of the generics discussion so I pretty much can see both sides as a valid approach. And it's a WIP and I know if one person writes it, it will be biased no matter how hard you try.
PS: I find the "straight to the point" comments much more useful - they are much easier to understand. So don't worry about it :)
I have been writing Go code professionally nearly every day for 6 months. Would I rather be writing C? Probably. Python? Not on a project of this scale. C++? Toss up. Java? No - doing far to much interfacing to C libs for that. Rust? Probably. Go is far from perfect, but honestly not that bad.
Either way, the example[0] I was greeted with on the homepage[1] is quite awesome. Does it have an actor implementation like Erlang? How does it handle concurrency?
0:
1: http://factorcode.org/Erlang has other advantages, though. 99.9999999% uptime and hot code replacement are easier with it, for instance. Their ideal niches don't completely overlap.
Once they do, you know not every "generics or bust" will be satisfied and the anti-Go hate will simply shift.
I say let the haters hate and keep generics as their focal point.
Which is the grown up, honest, thing to do.
But I'm not sure about the other thing you mention:
"Maybe but maybe the "Go needs generics" folks will just go away."
Why would they go away?
They ask for Generics because they have found some use for Go (say, because it has a OK standard library, or decent tooling and momentum compared to alternatives), but also found a need this feature.
Neither the "use of Go" they found, nor their need for generics will disappear if the language devs say "no". If anything, as stakeholders in the languge (a user is a stakeholder) they'd be even more pressed to change the language dev's mind -- down to the fork point.
the go-lang team at google is very smart. It's not that they have a huge amount of high-priority work other than generics. Why don't they address a summary of the sentiment on hacker news on generics ?
If you're a language developer, your life is made harder if you have to add generics.
And if your language gets all the people that are easily satisfied with something "static and faster to write than C" why go the extra mile?
Especially if your goal is not building a commercially succesful language, or an advanced language, or a language to solve 2014's problems, but just to hack on what you have created.
Besides, being a great programmer and a great language designer are two different skills. Would a language created by Linus be that good?
No, I don't know that.
Do you suggest they are lying and just hate go out of pure irrational emotion? (Despite some of the critics having writen thousands of lines in Go, or some like pjmlp have even had code in the standard libs).
Is there evidence to suggest that?
Does that hold true for other languages too? When people criticize a specific problem a language/technology has (e.g. Python's GIL or PHP's lack of proper language implementation with AST et al, SOAP for its verbosity, etc), is that out of "haters gonna hate" mentality?
Does that extend to that programmers cannot recognize constant pain points in a language, and those that complain about such things are irrational?
I find the idea expressed in the comment incoherent and illogical itself.
I'd myself be very happy with a hint of generics in Go, though. Something at the package level, that would be instantiated at "import" time (since the import keyword is supposed to hold some magic in itself) would have my preference.
Alternatives to these are just manual or semi-manual variants. For example, Go people seem to like the "empty interface" approach. Well, that is Java-style type-erasure done manually. The code generation approach is just C++-style instantiation done outside of the compiler.
[0] http://beza1e1.tuxen.de/articles/generics.html
See the ".Net Implementation" section here:
http://www.jprl.com/Blog/archive/development/2007/Aug-31.htm...
http://www.artima.com/intv/generics.html
I am not quite sure from the interview what happens about constraint types. It seems the reference is cast into an Interface type (IComparable) and used as such. In other words, boxing.
"... C# does strong type checking when you compile the generic type. For an unconstrained type parameter, like List<T>, the only methods available on values of type T are those that are found on type Object, because those are the only methods we can generally guarantee will exist. So in C# generics, we guarantee that any operation you do on a type parameter will succeed."
Edit (more explicit quote):
"When you say K must implement IComparable, a couple of things happen. On any value of type K, you can now directly access the interface methods without a cast, because semantically in the program it's guaranteed that it will implement that interface. Whenever you try and create an instantiation of that type, the compiler will check that any type you give as the K argument implements IComparable, or else you get a compile time error."
Furthermore (related to the CLR avoiding boxing costs):
"I'm just pointing out that we do fairly aggressive code sharing where it makes sense, but we are also very conscious about not sharing where you want the performance. Typically with value types, you really do care that List<int> is int. You don't want them to be boxed as Objects. Boxing value types is one way we could share, but boy it would be an expensive way."
C# does it differently to either - see here:
http://www.jprl.com/Blog/archive/development/2007/Aug-31.htm...
(The section marked ".NET Implementation")
Edit: And here's Anders Hejlsberg talking about the design:
http://www.artima.com/intv/generics.html
That, at least, is correct. Generics can be erased (compile-time only) or reified (generic type instances exist at runtime).
C# uses reified generics, and Mono creates (created?) a reification for each parameter typeset, Microsoft's implementation creates the same underlying reification for all reference types as a space optimisation, but it still uses reified generics.
There is a good helping of dishonesty in using C++ as the "reified generics" example though, as C++'s templates do significantly more than just reify generics (template specialisation, code generation, etc…) and that added to the interactions between templates and other language features makes them significantly more complex than e.g. C#'s, Rust's, Ceylon's, Kotlin's, and most other instances of reified generics.
- Eiffel
- Ada
- Modula-3
- D
- MLton
- .NET
- Rust
- Haskell
- OCaml
- Sather
- BETA
- CLU
Even if they overlap, it would be nice to see them being discussed. Not all generics are born alike.
Personally, I don't care any longer. It is still better than just plain C.
Go: still better than just plain C.
Even beats Algol 68: http://cowlark.com/2009-11-15-go/
Isn't the point of the article that Go doesn't actually beat Algol 68?
> The answer is that they're both very, very similar, with much the same level of functionality. They overlap more than they differ.
> Go has more in the way of actual features --- interfaces, channels, maps, defer , fallthough , proper closures --- but Brand X is more consistent with its features. […]
> Go, on the other hand, is full of corner cases. […]
> Most killingly of all, it's full of examples where they did not consult the literature. […]
> Brand X doesn't have any of these issues. Oh, it's got issues all of its own --- for example, the oddities in the reference type system; no varargs functions; no proper object system; the lack of little bits of syntactic sugar like keyworded composite structure literals... but it feels complete in a way that Go doesn't. I can use Brand X to implement nearly all the features of Go. I can't use Go to implement the features of Brand X.
> The only major features that Go has that Brand X doesn't are proper closures, and the interface system. And I'm afraid that's not enough.
> I would rather code in Brand X than Go.
Sort of. The point is that it's not modern at all, that it's actually comparable to Algol 68 without resorting to hyperbole.
In some ways, the article says Go beats it, in some other Algol wins.
Right, so Go does not beat Algol in the end.
And the article does conclude with preferring Algol to Go.
This seems to come up at every discussion about Go. What usually also comes up is how Rust is a much better designed language which solves real problems, while Go merely improves a little over C while leaving most of the same death-traps lying around.
Yet at the same time Go seems to have a much bigger adaptation than Rust. What's up with that? Are Googlers paid to create projects in Go, or are people genuinely happy to have anything which sucks less than C and stamped with a "final" badge of approval?
Well it has to do with Rust not even being 1.0 yet, whereas Go has been stable for 5 years...
Or with Rust changing some syntax in every point release for most of it's design cycle, so even early adopters had a difficult time.
Or maybe with Go having Google behind it, with the increased visibility it entails... (Rust, for one, never had huge Google Developer convention sessions exposing it to thousands of devs -- or any conventions for that matter).
And yet, we're already seen increased adoption for this pre-1.0 rust, major community playahs like Katz getting in the core team, more posts and libs every day, etc...
Go is about capturing dynamic languages' flow of development and put that on top of types and static compilation so you can improve early on performance and correctness (That doesn't even include inserting a "generic" way of coding concurrent processes).
Rust is much more about replacing C++ in features (from my little-knowledge POV): provide higher-level constructs to model your problem than raw C, with a heavy control over memory for maximum correctness and performance.
Rust may be better designed, yet I find it much more verbose and harder to understand than Go. While this may be a reason for the difference of adoption, a much bigger reason must be the difference in maturity.
The only features it has regarding that is GC, built-in hash-table and type inference. Hardly the gist of what makes dynamic languages flow a fast/nice one.
Plus Go was also advertised as "systems language" -- and besides, the same kind of programs Go is used for, servers, REST, network infrastructure stuff, etc, Rust will be used for too.
It's not like Rust is only (or primarily) for anything much lower level than Go.
No, the real feature is understanding that a language doesn't stop at the syntax, the tooling and the environment are extremely important:
- unless you want to build multiple binaries in one command, there is no need for any build helper.
- hack-compile-test cycles are really short, to the point you can run simplest go programs as scripts
- go has its own package manager, allowing you to manage dependencies extremely easily (not technically specific to dynamic languages, but in practice often associated with them) (Note that I'd prefer if everything was managed by my OS package manager, but we're not there yet)
The go creators (and community) has spent a lot of effort in improving the overall development environment, and this is a characteristic I have seen more developed in dynamic languages (again, I'm not saying that it's tied to a type of language, but I have seen this more developed with the pythubyerlascripts)
> It's not like Rust is only (or primarily) for anything much lower level than Go.
It's not, It's just targeted at different problems. You wouldn't use Go to build a game engine [1] (you need tight control over memory and have some realtime requirements), and I'm not sure you need the tight control that Rust gives you over memory when you build an image processing server [2] (you'll need concurrency primitives for this)
[1] https://github.com/Jeaye/q3
[2] https://github.com/ReshNesh/pixlserv
Why not?! Not all game engines need to be the next CryEngine or Unreal.
http://azul3d.org/
https://www.youtube.com/watch?v=cjn3twYB7xQ&list=UU7n_g2xDyS...
https://www.youtube.com/watch?v=uzQ2dnVac3Y&list=UU7n_g2xDyS...
Yet remarkably people are solving real problems with Go. Despite all of these dire predictions and pontifications on here by so many mortally offended by Go's existence, people are simply making solutions and moving on.
I don't believe I've heard of a single significant solution with Rust. I mean, I'm sure one exists somewhere, but it seems that it's caught in the advocacy trap of people talking about how great it is (overwhelmingly in Go discussions, it seems), instead of building solutions with it.
Servo is pretty much the motivating application for the Rust language. So you should keep your eye on that if you're interested.
That said, Rust doesn't have any real applications, because it's not out yet. The core team pretty much states, don't build anything significant in Rust atm.
It'll probably be out next year. Also Rust is a much more difficult language to grok, because it forces you to care about borrows.
That said, yes, before 1.0, you have to be a special kind of person to do such a risky thing. That said, Skylight can't really do it another way.
Also people do that with C, C++, Python, etc etc, so no need to invent Go at all, right?
This overall comparison might be a dead end, though. After all, we're comparing languages that effectively only have in common that they seem to have been spawned at vaguely the same time. Their philosophies are totally different.
Well, so far it looks like Rust resolves real problems in theory. In practice, I just see much more projects made in Go than in Rust. I confess that I didn't go hunting for Rust projects though.
Maybe it's the same as Mercurial vs Git. The latter is more popular, even if it's "worse", according to some.
When it comes to developing software applications, the really weak type system of Go is frankly just causing developer pain. IMHO, generics are the missing feature in this language, just try writing a generic map or reduce function in Go and the amount of code you have to duplicate is ridiculous. Interfaces and using relection is not safe and annoying and having all the XXXInt, XXXFloat, XXXMyType is crazyness too.
For what its worth, my dream language would be Julia's type system grafted onto Go.
This caught my eye because I recall you being someone who has vigorously criticized Go in every single Go discussion. A quick search and I can find lengthy critiques from you going back four years.
It very much seems like you haven't moved on.
https://groups.google.com/forum/#!msg/golang-dev/Zx2uSqvMS0Q...
If you read correctly my post, I just said I would like to see the discussion about generics take into account what other generic systems besides Java and C++ offer.
In those four years as you say, I have never seen such a document, even most discussions on gonuts went around Java, C++ and .NET implementations.
There are mentions that a prototype was made, yet the conclusions haven't been shared with the outside world.
If the designers don't want to implement generics, I can always look to Go as Active Oberon with C like syntax.
I see more value arguing about using Go for user space applications that are still made in C, just because the authors needed a compiled language than arguing about the essence of generics in Go. That boat has long left.
Edit: Just got to read Ian's opinion, quite interesting.
With Julia's type system you get a really good parametric multiple dispatch, which is a completely different (and more satisfying, to me) way to organize code than in Go. Go wouldn't be Go at all with Julia's type system.
If that's what you want, why not just program in Julia?
Although at least in Go and Java the downcast is checked and memory-safe, not so in C.
When you cast from void* to whatever type, the compiler will happily accept it and bad things will happen if the type is wrong. Maybe not right away, thus the program will bleed with corrupt memory.
With Go when you cast to the wrong type, a cast violation will occur and the program will panic right away.
But without the speed of C. D and Rust are both better than C and better than go, while still being as fast as C (ok rust isn't always as fast as C yet, but it is still faster than go).
In fact, gccgo does produce better code than the *g compilers
I remember the days when C code on home computers just sucked and most of us coded in Assembly for games, as C was a business language.
Compiler optimizations take time, even if sharing backends across languages.
No. You can clearly see that I am not doing that simply from reading my post. So, why the strawman?
> do you want slow programmers, slow compilers and bloated binaries, or slow execution times?
Easy I want "slow compilers and bloated binaries".
In my experience "bloated binaries" in the real world aren't as bad as one would think, since instruction cache problems only appear when your working set is huge, not when your total binary size is huge.
Equating big binaries with slowness is simply misleading. What's next, increased download times?
> The generic structures are less useful than a concrete one.
Without generics you can't separate algorithms from data structures so everything is much less useful. If the members names are worse, sub-type then rename symbols.
> Generic structures can be less optimized than a concrete solution.
False. That "concrete solution" not only has to be written, but it will also perform identically to an instantiated template.
And type erasure is not a solution, it prevents inlining, constant folding and hoisting. Eg: In C++ std::vector<MyObject>::operator[] has 1 indirection, while using a generic C void* container would have 2. Guess which one is faster?
> Generic algorithms can be less optimized than a concrete solution.
sigh
Also wrong since generics do not prevent specialization if needed. See: static if in D, template specialization in C++.
The "bloated binaries" part is especially odd given the state of the Go compiler's optimisations (or lack thereof):
Generics is not the only way to separate data and algorithms. I agree that it is possible to sub-type/embed and rename - although looking at generic code, I rarely see that happen.
> That "concrete solution" not only has to be written, but it will also perform identically to an instantiated template.
When you know where exactly the code will be run, it can be possible to create a better package taking into account the context where it will be run. By concrete, I mean a package in a real-world problem.
Basically, abstract things are harder to optimize for a particular case.
Do you see any way other than the 3 ones discussed in the OP article under the label "Generics"?
> When you know where exactly the code will be run, it can be possible to create a better package taking into account the context where it will be run. By concrete, I mean a package in a real-world problem. Basically, abstract things are harder to optimize for a particular case.
I could agree with that if time wasn't limited, but imho building custom data structure/algorithms for each problem will precisely leave less time to optimize for performance since reaching a working state first will be longer without reusable abstractions.
The one I mainly meant was interfaces. Of course there are also DSLs and code generation.
> I could agree with that if time wasn't limited, but imho building custom data structure/algorithms for each problem will precisely leave less time to optimize for performance since reaching a working state first will be longer without reusable abstractions.
I mentioned that in the summary. It's about tradeoffs. Even if all the cons/pros are listed people could still come to different conclusions, because people value different things. And there's nothing wrong about it.
The amount you need to reimplement depends on the amount of available third-party packages - the more good packages there are the less you need to build those custom data structures.
Just being able to do very light type insertion and the transparent code generation to go along with it would go a long way.
Now days I think that adding generics to Go will be disaster - Go lacks sound type system and piling generics on top of it will make the thinks even worse.
If this issue is so important to people, don't use Go, use C++, Java, D or Rust. Don't waste your life arguing about something you'll never use, it's not a productive use of your time! Think about how much time you could save!
Alternatively, propose a solution to the problem and try and gain some traction to get it implemented into the language, or fork Go and implement it yourself. There's evidently a hunger out there for generics in Go, but everyone keeps assuming the responsibility lies with the Go Core team who's priorities seem (right now) to be in the optimising GC/compiler area.
And exactly as you said -- for those people so offended by this: Move on. Use something different. Go isn't for you. As someone who has integrated Go very effectively into my life, I'm rather glad the Go team isn't listening to every railing fanatic and their pet issues.
And then:
>I'm rather glad the Go team isn't listening to every railing fanatic and their pet issues.
Oh, the irony.
Why people assume that only people NOT using Go are complaining, it's beyond me.
Or why people assume that if you like features X of a product, and would like it to have feature Y, you shouldn't.
Which is strange, because a post about Haskell tends to be about Haskell. A post about Go tends to be a lot of people telling everyone how Go is unlike, to its detriment, other languages. Great -- go use those languages.
Why people assume that only people NOT using Go are complaining
HN doesn't have a large community of commentators, and you get to know the common personalities. There are absolutely people building solutions in Go who might think "it would be nice to have generics", and indeed that's exactly why they've never been discounted or denied by the Go team, simply deferred. But these discussions are dominated not by those people, but instead people telling you why Go is not Rust is not Haskell is not C# is not...
What's the point? We have a lot of languages and environments to choose from, and we do. Why are people seemingly threatened by Go? It's bizarre.
Generics are nice and they're important, but there are a lot of nice and important attributes to a language, and they have bigger priorities. If generics were critical to my work, I simply wouldn't use Go (and indeed, for some subsections and components, I don't for that reason. Life goes on. Different tools serve different needs).
I don't think anyone's threatened by Go. I think some people are disappointed that Go's designers chose not to add a lot of extra type-related features, including generics, for some extra effort on the part of the language designers but very little cost (and much benefit) to the language users. If this was an unused toy language there would be no problem, but the existence of lots of code written in any language will have a small cost on other developers for social reasons, even if they don't use that language.
Yet your final sentence says exactly the opposite, indirectly. As if Go is some sort of scourge that these gallant guardians of language must fight.
Further, if you assess the simplicity of adding generics, and it is at odds with the actual people creating the language and tools (among whom are some of the most respected names in this industry), that's a point when you should stop and reassess your own, outsider-looking-in perspective.
I hope to one day experience a Go thread with no appeals to authority with regards to the language inventors/implementers.
The appeals to authority are not always used in a fallacious way (like the informal logical fallacy). But I don't personally like it, and I think it is intellectually lazy. It's better to let the product itself stand or fall on its own merit.
The appeal to authority fallacy is if I measured the acidity in a lake over a long term period, and then presented my findings and someone declares that such can't be the case because the Environment Agency says it's fine. They're undermining my objective, demonstrated efforts with a hand waving appeal to someone else. If, on the other hand, I casually and lazily said "I think that there lake is acidic, no matter what anyone tells me", it isn't an appeal to authority to point out what someone who is undoubtedly in a better position thinks and actually knows.
In this case someone offhandedly, based upon nothing but the fact that they seem to want it to be true, declared the ease with which the Go team -- you know, the actual people working on it -- could add generics. No fuss, no muss. It isn't an appeal to authority to point out that they are in absolutely no position to make such a claim.
Like I said; I didn't mean that you, or necessarily people in general, use the appeal in a fallacious way. But it was my fault for not making that clear to begin with.
This is a general note, not thinking of any post or argument in particular: I can hardly count the number of times people who advocate Go will bring up the fact that people like Rob Pike and Ken Thompson are or have been involved in the project, and how smart they are and even 'how can I even question their work, given their skill and talent'.
Most respected names in the programming industry.
Not in the language/compiler design industry.
What you write above shows that HN users don't have the same behavior with other languages indiscriminately...
So, doesn't this sound like maybe it's Go that's problematic and not HN user behavior? (see also below)
>There are absolutely people building solutions in Go who might think "it would be nice to have generics", and indeed that's exactly why they've never been discounted or denied by the Go team, simply deferred. But these discussions are dominated not by those people, but instead people telling you why Go is not Rust is not Haskell is not C# is not...
And why shouldn't they? (Well we)
Go has design problems (beside the lack of Generics) the same way PHP has or JS has.
And it's criticized in a similar manner as when JS is brought up its type coercion is mentioned, or its global by default scope, its confusing this context, etc.
Now you might agree with the above or not. But what you ask is essentially for people that perceive it that way, to not speak, and just let it go.
Isn't the whole point of HN discussion (including about languages), criticism etc? It sure is not about celebrating all languages just because they exist...
And you're right, the Go core team does not seem interested in adding generics anytime soon.
Personally, and this is my opinion, I find myself wondering if old school hackers like Thompson, Pike, and Fitzpatrick (and others) find themselves not needing generics, or that slice/maps are enough, maybe I should ponder that for a while. Ie do they know something I don't? Edit; or maybe the type of software they're creating with go is different than I as a generics user, am creating?
Or maybe their work is mostly creating Go, not using it, so they wouldn't care?
https://github.com/robpike/ivy
I haven't used Go at all yet, but I'm interested in hearing more about it the good and bad parts, along with other languages. Telling others to not complain at all doesn't do any favors.
Remarkably there has been close to zero promotion of Go on Hacker News. Occasionally various incremental update things appear on the front page, just as there are updates on C# 5, the next version of Java, etc, but there have been shockingly few "Why you should use Go" type posts.
People just use it. They don't advocate it. But when it comes up, inevitably it becomes an advocacy starting point for alternative languages. Somehow every single Go discussion, even about the most banal thing, ends up being about Rust, Haskell, F#, and so on.
Imagine if you were used to using power tools, but all the jobs available forced you to use hammers, screwdrivers, etc. Wouldn't this make you a little angry or at the very least disappointed?
It's one person's summary of a very long and convoluted conversation. That's all.
I try my best to be objective - obviously this isn't easy. This is intended to be a review of both sides of the arguments. The generics discussion comes up again and again - I made this document to stop arguments going around in circles.
I will go over all the suggestions and add any missing information. Of course, if you have some good overview of some particular generics implementation you can PM me, or simply add that as a comment.
2. generics are about type-safety, not codegen. Codegen can be added on top of parametric polymorphism (e.g. C++ template specialisation), but is not required by any means (C# or Rust don't do any generic-based codegen beyond reification)
It's 2014. Generics are a settled deal in programming languages. It's not some syntactic sugar or something the value of which is stull undecided and its advocates might "lack an imagination".
>Use lang2 if you love it so much. Seriously.
Well, some do. That doesn't mean they don't get to complain about something they perceive broken in Go that could be better. Especially if they could use Go (to take advantage of other benefits it might have) if it weren't for those shortcomings.
In general, the statement is analogous to "if you have a problem with X in america, why don't you immigrate", which is silly. If you have a problem, you criticize, and you try to change that. You don't just dismiss everything that's also not problematic.
I don't mean that the manually specialized version is faster than automatically specialized template. Rather that this encourages to not write abstract data-types, but rather concrete types that solve a real world problem - editor.Buffer vs. gap.Buffer. Once you have a real world problem it can optimized much better for that use case.
https://groups.google.com/d/msg/golang-nuts/smT_0BhHfBs/MWwG...
Boxing and generics are orthogonal concepts.
Also: does auto-boxing even make sense in Go? Between the fact that Go is statically compiled and that there isn't an int / java.lang.Integer dichotomy to resolve, I would assume that the compiler could just do whatever it needed to with primitives, regardless of generics.
Yes, and already happens: interfaces cause wrapping of their content[0] and go does basic escape analysis and automatically allocates things which escape a function on the heap rather than the stack[1]
[0] https://golang.org/doc/faq#nil_error
[1] https://golang.org/doc/faq#stack_or_heap