A language's core syntax should not privilege its standard library above other libraries.
I'm not so sure. Smalltalk had this in spades. There is a downside to this. Giving a bunch of 20-somethings the full power to basically change everything can result in code-bases which suffer from the chaos of over exuberant hubris. Go is deliberately favoring a certain set of conventions. To do this, they are also deliberately making it harder to change the language from within itself.
The way to discourage people from "changing everything" is to make the standard library ubiquitous and to use encapsulation to prevent people from changing its internals. If Go had generic maps in the standard library, and you couldn't get access to their internals, then the effect would be exactly the same as the built-in map that Go has today. You see this in other languages: very few people create custom dictionaries in C#, for example.
Maybe. That's like the apocryphal story about the student asking a question if a particular proof step is really obvious, so the professor goes across the hall, derives stuff on the other blackboard for the next 30 minutes, then comes back into the lecture hall saying, "Yes, it's obvious."
Maybe you increase the consistency of the language from one point of view, but what happens from the perspective of each individual project? What if this leads to projects where templates have been used to create 3 different template based "little languages" to make X, Y, and Z easier? You had 3 problems, but now you have 6. The oft written reply to that in these debates, is to limit the power of the template system -- but is that a robust goal under the group dynamics of the language community? I think not.
One subliminal goal in the design of Go seems to be about privileging certain conventions to avoid a babel of roll-your-own conventions in large projects. This is all across the language and even in the toolchain.
But bona fide parametric polymorphism doesn't have this problem. ML's design is a constructive proof that you can have a reasonable degree of abstraction without compromising usability.
Being opinionated works well for some teams --- I get that. You don't need the language spec to be opinionated when you can get the same result through mechanisms that don't affect everyone.
It's just like when we're writing code: don't make global changes to achieve some local effect
This deserves a bitter laugh. For large, long-lived codebases, where there have been effectively many different teams and a number of different managers, the standards, coding styles, and tooling are almost certain to change. If you know of a large corporate codebase where this is not true, please tell me about it. Hell, you need to write a paper about this and start giving talks at conferences!
It seems like the Go maintainers are trying to solve this by moving such conventions from the level of individual project to the level of the language community. I think there is merit in this. For one thing, I suspect this will create greater social and intellectual cohesion across the language community as a whole.
Sure, but I've never been convinced consistency matters at that scale. I think it's enough to be locally consistent; Windows doesn't seem to be hurt by its codebase containing practically every style you can imagine.
> level of the language community
Sure, but if you make the language too Procrustean, you risk making that community smaller, even if it's more cohesive. Personally, I'd rather have a bigger community, even if the members don't agree on everything.
In C++, it can result in the occasional memory management paradox to work through. (In addition to the background cognitive load for watching out for such.)
This presupposes that change is bad. In many (most?) codebases, the change in style is good. Gecko, for example, has survived as long as it has because of the gradual migration away from '90s Don Box-style componentry toward modern C++.
Sure, such a thing can be good, as in, it's a good trade off. Pulling a tooth can be good in this sense, as it's better than getting further infections from an abscess. But here's the thing about trade-offs -- really you want to avoid having to make them in the first place. (Dental hygiene.)
I'm working in a C++ code base right now. It's like an archaeological dig, with each "layer" corresponding to a major revision of C++ style. It's "good" that parts of the codebase are more modern, but the price for this is the cognitive load of language mode switching. (And occasionally, memory management conundrums.)
To quote Theodore Roosevelt, "A man's hindsight is as good as his foresight, and if he doesn't use it, it's a darnedsight!" I'm saying that the Go maintainers should take a look at the historical pitfalls of other languages, like C++, and try to use that information to chart a smoother path. From listening to Rob Pike talk about his motivations, I think that's exactly what happened with Go.
I can't think of a single language that had problems with incompatible core collections due to having them be in the library as opposed to directly in the language. The closest thing I can think of is Haskell's explosion of String types, but one of the lessons to take from that is precisely the opposite of what Go chose—it's pernicious partially because the suboptimal String is baked into the language, and the overloaded strings extension is necessary to correct this!
You assumed wrong.
I can't think of a single language that had problems with incompatible core collections due to having them be in the library
So? Who said that? Not me! And actually, Smalltalk did have some collection incompatibilities across vendors. (#at:put: returning the inserted value in one impementation while it returned the collection in other ones.) But the implementations of those weren't completely in the library. (You could treat them as such, however.)
If it trusted the user just a bit more, it would be making many of the same mistakes other languages make, which Go is trying to avoid. You're free to go and use some of those other languages.
It's a particular trade-off. Some opine that it's not the right trade off. You disagree.
Complicated architectures full of abstraction are a mistake, which you can limit without sacrificing the ability for me to make a type-safe generic function.
From what I've seen, the other mechanisms for limiting over-exuberant abstraction don't work well enough over the long term, at scale.
Can you name a specific example of people using ML-style generics (i.e. no typeclasses, no module system) to achieve "over-exuberant abstraction"?
Can you explain with an example that would apply to Go how introducing generics to a language was making a mistake? Be specific.
These things are epiphenomenal, and have to do with what happens in large codebases over a long time, with lots of programmers. It's a fallacy to suppose that neat StackOverflow sized examples are some kind of a evidence gold standard. The problems I've encountered with C++ templates have to do with the interaction of several things at once, in places I'd have to dig out of version control, in codebases I can't share. So no, I'm not signing up for doing that work for you for free.
(I don't think there are any such examples, because simple ML-style generics yield a lot of power for negligible drawback, and I hope that future versions of Go add them.)
Hence why I decided to stop arguing about Go's lack of generics and rather advocate it for those that search for a C + GC with improved type safety.
For the rest of us there are better options.
BTW, you might be interested in https://oden-lang.org/
Yeah, how dare he have an opinion that runs counter to the known truth. He has attacked the Body. He is not one with Landru.
It's really hard to justify making a new go, python, lua, etc. implementation when the primary ones are stable and support a large number of platforms.
And adding language features by altering syntax/semantics, you might as well produce an entire new language rather than deal with the headaches of being partially compatible and having to track the original over time.
It would also be a massive effort to add generics to it at this point, because of the design choices the team made early on.
Um. Well ok, maybe but not likely, as pointed out by a sibling comment, but it is used by a large fraction of companies that deal with services on an enormous scale - Google (obvi), Dropbox, Cloudflare... here, better than copy/paste: https://github.com/golang/go/wiki/GoUsers
You're going to recognize an awful lot of those companies.
Edit: not sure if it counts, but the number of companies using software written in Go in mission-critical ops is enormous (see: Docker).
Not really. The easiest approach would take like a day for PoC.
Maybe I could do some kind of poll of people hitting this or other rough snags that would be solved by generics or something similar? But I am afraid that (rightfully so) go fans would feel the poll could be overrun by people who only casually dabbled in go. Same with counting the many complaints of people every time this topic is brought up- the general response seems to be that they don't want that kind of programmer here anyways, so go away.
It's not clear to me that the language would be better if the project stewards accepted more feature requests, though. It is incredibly difficult to hold the line against feeping creatures and a noisy minority when the benefit of simplicity is diffuse.
I'm thinking things like min/max, which are really distracting and are concepts that are more easily expressed as a concise function rather than an explicit loop.
Go is a great C replacement for any use case where using a GC is an affordable option.
Other than that, there are lots of other languages with AOT compilation to native code and better abstractions.
For everyone else, there are better options.
That's exactly what a helpful language does:
(0) Define a region in the design space that contains the program you want.
(1) Conveniently tell you when you have accidentally stepped outside of this region.
> more complex.
How are you measuring this? I know of two good measures, and neither favors Go:
(0) The size of a formal semantics. Go's particular feature set suggests looking at Featherweight Java (a subset of pre-generics Java specifically designed to be amenable to formalization), and, well, FJ's ratio of static guarantees to language size is very low compared to most typed lambda calculi (on which ML and Haskell are based).
(1) The number of special cases in the language's design. Here Go fails miserably, due to the sheer number of built-in types and functions that require hardcoded support.
Sounds pretty bondage & discipline to me.
Imagine you found it intolerable to have an interpreter-dependant -- not even /bin/sh -- source file in your package directory (said another way, anything but .go source files in the package directory), but at the same time needed to somehow sneak in the functionality of a rudimentary shell script. The answer? Weave it into a .go source file and the Go toolchain will be able to pick up on and execute it (on demand, never automatically).