I'm a big Go proponent, and I'm pretty "meh" on generics. They'll make some code easier to read and write, but they'll make a lot of code a lot harder to read (because contrary to popular belief, "readability" doesn't always increase with abstraction or terseness).
That said, sum types would genuinely be helpful--the workarounds are error prone (making sure all cases are handled by code) and inefficient (unnecessary allocations if the implementation is backed by interfaces or wasted memory if it's struct with a field per variant). Note that simply supporting sum types doesn't guarantee that they'll be implemented efficiently--I'm afraid of the possibility that the Go community might settle for an interface-based implementation rather than a union-based implementation.
On balance, however, Go is still the most productive language I've ever used (and by a healthy margin). People make way too big a deal about type system minutia (having a basic type system is important, but going too far beyond that can be counterproductive) and they are far too forgiving for things like bad tooling, poor performance, or impoverished standard libaries.
For example, say you want to separate a slice into two slices, one that contains matches and one that contains everything else. Right now, you'd just write:
var matches, mismatches []whatever
for _, x := range xs {
if x == condition {
matches = append(matches, x)
} else {
mismatches = append(mismatches, x)
}
}
But soon, people will be golfing that down to: matches := slices.Search(xs, func(x whatever) bool { return x == condition })
mismatches := slices.Search(xs, func(x whatever) bool { return x != condition })
This is twice as slow, but it's less lines so I have a feeling that people will have an uncontrollable desire to use it as often as possible. I hope that my feeling is wrong.(Do note that slices.Search is not a real function yet.)
matches, mismatches := slices.Partition(xs, func(x whatever) bool { return x == condition }) matches, mismatches := slices.Partition(xs, func(x) bool { return x == condition });Though now it's much easier to make a "matches, mismatches := slices.Split(xs, func(x whatever) bool { return x == condition })" helper, so that improvement even reduces the number of lines further.
This, mixed with the "Partition" function proposed by sibling comments (which would only iterate once) could lead to code that's faster than the regular approach, while also being shorter to write.
On the other hand, it's not as free as it seems, as now developers have to know the Partition function, and how it works. It may also have pitfalls that I haven't considered. As always, it's hard to find the perfect sweet spot between how much stuff developers should know and how much stuff should be explicit in the code all the time.
I don’t think generics will greatly damage what Go has going for it, but I hope it also doesn’t block the way for better error handling, sum types, and other things that could potentially improve code robustness.
I'm sure generics are going to be useful in some instances. Also of note 1.18 is bringing built in fuzzing, which is nice.
Where I'm getting my margin of benefit over writing tooling in say Python or Ruby, or other languages that fit this domain:
1. Fewer choices: The build system, module layout, and networking APIs don't lead me down the road of trying to figure out which library is best, thereby wasting time learning different libraries. I have never had any desire to google around for external libraries etc. Everything seems to just work fine.
2. Clear design intent: Once you've figured out what you're supposed to do with language idioms, like passing around ReadClosers or whatever it becomes super easy to read code written by other people who've also come to understand the intent. I don't notice this until I try to go back to doing something in Python and remember how vastly many approaches folks take to the same problem (like how should a database object be passed around.)
3. Works for my workflow: I spend most of my life in terminals fussing with serial communication, or fighting with mqtt or an APK that won't load like I expect, etc. The tooling with Go for nvim is incredible. I've found that now that I've gotten fairly fluent in the language I will often crack off small scripts in Go to automate dumb things I don't want to do in Bash or whatever, this is maybe an anti-pattern, depending on who you talk to.
Summary: My big margin over other languages just comes from consistency of design and interfaces and the fact that Go integrates with my workflow. It's very easy to figure out how to do a thing the first time I'm working on it. It's also easy to throw together a one off tool in an afternoon, use it for two weeks, push it up to my github and forget I did it until the next time I need it.
YMMV
I do the same. Most everything you need for quick scripting against services and the like is built in. No hunting down libraries. Then the build output is a fully contained binary that can run on my m1 mac, x86 server, whatever.
> It's also easy to throw together a one off tool in an afternoon, use it for two weeks, push it up to my github and forget I did it until the next time I need it.
And when you do go back to it, the code is easy to read and understand.
One example I'm currently confronted with: I'm writing a long-running backend task in PHP, and since the rest of the backend is based on Laravel, it's using Laravel too. The script doesn't really do much, and as far as I can see there should be no memory leaks, but unfortunately it can still only run for ~ 3 hours before it quits because of running out of memory. I'm sure it will be real fun debugging that. Makes me want to switch to Go...
1. Working with basic static type support helps me to not worry about silly errors which helps me move faster. Beyond that, it helps team members understand each other's code correctly--when working with dynamically typed languages, type documentation would often be incomplete or become outdated/incorrect and I would spend a lot of time trying to understand the correct type. Moreover, static typing enables a lot of IDE tools that save a ton of time (e.g., goto definition). This is probably more of an advantage for more complex applications rather than simpler CRUD apps.
2. Performance. I've worked on a lot of Python projects where we've spent a lot of time optimizing, and all of the options for optimizing Python have hidden pitfalls (e.g., typically "just rewrite the slow parts to use multiprocessing/C/etc" end up slowing your program down because the costs of marshaling data exceed the gains never mind the costs of maintaining that code). Idiomatic, unoptimized Go is already 10-100X faster than Python and you can pretty easily squeeze more performance out of it via parallelism or moving allocations out of tight loops). If application performance isn't important to you, then this probably won't be particularly compelling.
3. Deployment artifacts. By default, Go compiles to single, reasonably small, static binaries so you can pretty easily build and distribute command line tools to your entire team, you can build tiny Docker images (tiny images mean shorter iteration loop in a cloud environment), etc.
4. Simpler Docker development workflow. With dynamic languages, there's not a great way to iterate. Normally, you'd want to mount your development directory in a volume, but Docker Desktop (or whatever it's called these days) will eat all available CPU marshaling file system events across the VM guest/host boundary, so you end up having to do increasingly convoluted things.
5. Employing and onboarding. Anyone from any language can be productive in Go in a few hours. No need to restrict your hiring pool to "Go developers". Moreover, for other languages, it's not sufficient to merely know the language, you may also have to know the framework, IDE, build tooling, etc that the team uses. The ramp up time can be significant, and this can be a meaningful problem in an industry where people change teams or even companies after a few months or years.
This list is pretty narrowly fixated around dynamic languages because I've done more Python development than I have Java/etc and also because your point of reference is PHP. It's hard to compare Go generally to all other languages.
You speak for yourself here! Miles of copy pasted code, slightly altered who knows how, impossible to refactor without either using code generation or eschewing type safety altogether was my experience. Its impossible to write entire classes of general purpose libraries with type safety! Its just beyond belief to me that "copy paste" is an actual, bona fide best practice in the community, or that a 'map' function is unexpressible!
> There's also a bit of an anti-expertise "who are you, Go creators, to say you know better than me how to design this program/engage in software development?"
Go the compiler/runtime is super impressive! Cross compiling small, binaries with a switch is awesome, so kudos! Go, the language, on the other hand, sucks; it actually sucks so much, that historically, we couldn't even write libraries to work around the things that suck. That is frustrating!
Go is really s souped up version of C. It's a design rooted in the 70s with some fixes to make it a good language for writing small networked apps.
Insofar as that goes¹, the language is fine.
However the creators of Go responded to critiques of the language in a patronizing manner and talked down to their own programming community at Google as being unable to handle complex languages.
Basically the Go community got the exchange off on the wrong foot. Personally, I sense egos were at stake. They created a language which is simple by design, but then still wanted to claim a sort of opinionated superiority for it.
If they'd said, "oh go is just a simple little language we enjoy using for such and such, perhaps you will too" then people who don't like Go wouldn't have felt provoked.
1: Pun not intended.
I’m partial to a different interpretation, that’s more like “go doesn’t require as much thinking as Haskell, so you can use your thinking for the problem you’re trying to solve instead of the language”.
With that interpretation, go is better not just for stupid programmers, but even for the smartest programmers.
Anyways, I can’t give the final word on this question, but those are my two cents.
I think you're being too charitable here. Rob Pike is a very smart and articulate guy. If he had wanted to say "go doesn’t require as much thinking as Haskell, so you can use your thinking for the problem you’re trying to solve instead of the language", then he would have. Instead he took a dig at google employees. Make of that what you will, but I don't think it's case of him mean anything other than exactly what he said.
Because it's not that simple, and no one likes being judged and talked down to.
Indeed. Then there was that whole controversy over the naming collision with the Go! language. Basically there's an unspoken etiquette in the PL community that there should be zero name conflicts between languages, as there are enough good names out there, it really shouldn't be an issue. The Go! language had been around for a long time, and I realize it was an obscure, little known language, but that's true for 99% of languages out there. So the idea that Google can just invent a language out of the blue with the same name as yours, after you've been using the name for a decade, and then just use their size and clout to essentially destroy your language is.... well that's unsettling to people in the PL community. The way they handled the situation didn't exactly win over hearts:
The naming similarity is unfortunate. However, there are many computing products and services named Go. In the 11 months since our release, there has been minimal confusion of the two languages, so we are closing this issue. Status changed to Unfortunate.
https://github.com/golang/go/issues/9#issuecomment-66047478Basically a sad trombone. The image of Go has never recovered in my eye.
Can you provide examples of that? Because the closest thing I can remember is Go creators saying that C++ had too many features interacting in weird ways, and that they wanted to avoid that. Which is a perfectly normal design goal.
> The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
The source seems to be MSDN, but the link has bit-rotted. https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fr...
This attitude I think is the reason most experienced deveploers I've worked with are put off by the language. You don't have to be a "researcher" to understand sum types or generics. There's a large part of the industry that thinks we should limit ourselves to concepts that can be understood by a beginner, and I think it's holding the industry back. I can't think of another industry where people think this way.
> This attitude I think is the reason most experienced deveploers I've worked with are put off by the language.
This is not my experience. Of the six team leads with whom I've discussed the language (after they've had some experience with it) only one was put off by it. The rest were either glad that “something simpler is finally here” or were of the opinion that Go is “just another language”.
(Interestingly enough, the one person who was not impressed was a big fan of purely functional programming, while all others were C++/Java/Ruby kind of people. Which is consistent with my own personal observation that fans of purely functional languages tend to dislike Go, for the most part.)
It's not only your personal observation (though I would remove the "purely" from "purely functional languages). A good indication of that: on the Go 2020 survey, the 6 most popular responses to "Which critical language features do you need that are no available in Go?" are features that functional programming languages have. All of these features are in ML and its descendants (SML, OCaml, Haskell, Scala, etc).
The graph: https://go.dev/blog/survey2020/missing_features.svg
The article: https://go.dev/blog/survey2020-results
In a way, you can see that the people using these languages that may appear very different are converging to the same "ideal". And that's not a surprise, both languages have a very Unix-y origin, and origin in Pascal/Modula. Go added to that CSP, OCaml added ML. These days OCaml is adding things to better handle the CSP part, and some people are asking for Go to better handle the ML part.
I've never fully understood this weird obsession either. You'd never hear a group of master craftsmen like plumbers or masons talking about making their tools and trade more "beginner friendly". They naturally expect beginners to learn the trade and eventually become masters themselves.
The cynic in me thinks it's large corporations that are pushing the whole "beginner friendly" narrative as a way to keep employees both 1) lower-skilled and 2) productive. If you help beginners develop into intermediate and then advanced programmers, guess what? You have to pay them more.
That doesn't seem like a good argument with regards to Go specifically, since Go is among the most high-paying technologies, at least according to Stack Overflow surveys[1]. At the same level as “LISP” (which Lisp is it, StackOverflow?) and only slightly below Rust and Scala, both of which are way more complex languages.
[1]: https://insights.stackoverflow.com/survey/2021#section-top-p...
I like go just fine, and I'm learning it as we speak. But I think the high pay scale has more to do with *where* it's being used more than anything else.
Go is very popular in SV, whereas most fortune 500 and other legacy companies are java world.
My issue is more with this current obsession that everything must be beginner friendly. The fact is beginners don't stay beginners very long, so it's a dumb group to optimize for.
Basically I gleaned this impression from the go message groups more than 5 years ago.
Go is pretty nice, but IMHO, it's no masterpiece.
There's a reason we keep copying C like syntax, and it's not just familiarity.
C could be criticized on many points, but in my opinion it was a brilliant case of language design: Only 30 keywords, the stdllib was a separate thing (not common at the time), great as a systems programming language.
If you think of C of the 1970s as a sort of souped up macro assembler, you're not far from the truth, and it was wildly successful, also, because of what it left out.
Go might have had similar ambitions, but the execution was not thought out as well. If you want to design a truly great language, it turns out it's not enough to merely leave things out.
I got the sense that the Go people wanted to be taken seriously on the same level as great achievements like C and UNIX, but the creators quickly saw through the incoming critiques that they hadn't pulled this off.
You know when you create something and then receive criticism? If the criticism is clearly wrong or due to a misunderstanding, you're not upset about it typically. You explain that the misunderstanding, and typically the person critiquing says, "ah ok..." and moves on.
But the critiques which really get to you are usually the ones which you know deep down are right. I think maybe this is why the creators of Go seemed so touchy about it.
EDIT: And for the record I program in Java and Rust mostly, a bit of Python too. I hate C++ (a sprawling mess) and also don't give a crap about Haskell or monads.
One of the creators of Go is Ken Thompson.
"I did the first of two or three versions of UNIX all alone. And Dennis became an evangelist. Then there was a rewrite in a higher-level language that would come to be called C. He worked mostly on the language and on the I/O system, and I worked on all the rest of the operating system."
a) why are there such short variable names, such that you see calls like b.c.d.Method() all of the time?
b) why is there no Set type?
c) why is date/time encoding/decoding inconsistent in json marshaling/unmarshaling (versus other types)?
But with generics, methods and interfaces, proper strings, maps etc., first-class functions, built-in concurrency, a module system, automatic memory management, a well-rounded standard library and so so so much more.
Before any rational analysis and criticism can begin, we need to look at the context of these arguments. I'll do that through some of the comments on this post.
- Issues people bring up are dismissed as non-issues or as personal preference / opinionated, regardless of their impact.
- Critics are asked for rational analysis, but proponents are allowed to use something as airy as "simplicity", a word that has essentially lost all meaning in programming.
- Missteps by representatives / stewards of Go are given the most charitable interpretation, critics less so.
- Finally, in so many cases, counterarguments to critics are dripping in condescension.
I don't think folks are going to get rational analysis. I also don't think they actually want it, whether they realize it or not.
- Boilerplate increases the surface area that a bug can hide in. The fact that most of the boilerplate is around error handling is especially worrying. Yes, the flexibility of "Errors are values"[1] is nice. But I also don't know any languages where errors _aren't_ values, so the main value add seems to be reduced boilerplate compared to individual try/catches around each function call.
- Go manages to repeat the Billion Dollar Mistake[2]. Things like methods working on nil receivers is cool, but not worth the danger or messiness.
- Even worse, for a language that claims to value simplicity, the fact that nil sometimes doesn't equal nil[3] is... honestly, I can only consider that a bug.
[1]: https://go.dev/blog/errors-are-values
[2]: https://www.infoq.com/presentations/Null-References-The-Bill...
(look, i know you understand how exceptions work, please bear with me)
yes, Exception objects are technically values, but you don't return them to the caller; you throw them to the, uh, catcher. basically, you get a special, second way of returning something that bypasses the normal one! but in the EaV approach, errors just are returned like every other thing.
the uniformity of EaV comes in handy when doing generic things like mapping a function over a collection - don't have to worry if something will throw, because it's just a value! and that lets you go to some pretty powerful places w.r.t abstraction: see e.g. haskells `traverse`.
but yeah, EaV needs some syntactic sugar to reach ergonomic par with exceptions, otherwise you get if-err-not-nil soup :P
Returning errors makes many things more manageable, definitely. But where they really shine, like in the mapping example, isn't possible in Go. Unless I'm mistaken with how go generics work.
(By the way, I'm a huge fan of how error handling works in Rust and other related functional languages. Definitely not advocating for the classic way of doing Exceptions).
oh yeah, definitely! Go's version of EaV with multiple returns is pretty lackluster compared to a proper Result type. afaict it's kind of "the worst of both worlds" -- all of the boilerplate of plumbing errors manually w/ none of the benefits.
- Rational reasons (frustrations with the language design) are briefly acknowledged but then some reason left by the wayside
- It is suggested that people don't have principled objections to $lang; they will "latch onto" something else if $misfeature is fixed
- Simplicity, a universally praised quality, might be the problem
- ... Because it can "humiliate" someone who has wasted time on needless complexity
- "$lang has opinions", which all languages have whether they acknowledge them or not. Why should this bug people especially when it comes to $lang?
- "Anti-expertise" for some reason, even though creating any "enterprise-ready" language takes expertise and many years of effort. (This also seems to be at odds with the "anti-simplicity" point?)
- ... But aren't then people who argue for $lang also anti-expertise? They are after all taking a stance on something that they are not experts at; they should take a neutral stance and defer to the experts
I would say that it is rude to suggest that someone might not really be arguing against X and instead ask what they are really upset about, since the follow-up questions and implications are absolutely never flattering. They inevitably just dive into the gutter with suggestions like "maybe simplicity humiliates you".
- Extensive laundry lists of frustrations, sharp edges, antipatterns, and ways the language makes it trivial to write incorrect code are dismissed as nits or minor annoyances.
- Missing features that have no way of being worked around are dismissed off-hand as unnecessary complexity.
- The language authors repeatedly ignore mistakes learned from other language projects and rush headlong right back into them, with predictable consequences.
- Verbosity is mistaken for "simplicity" while bugs scaling roughly linearly with lines of code is quite literally one of the few reliable bits of data we actually have in this industry.
You forgot to add the part where, once the features are added after all, they stop being "unnecessary" complexity :)
Go is Blub (http://www.paulgraham.com/avg.html). I think the industry is in a place where Blub makes sense! Lots of VC money is floating around, and schools are training a lot of people to hire with that money. Commercially relevant ideas most often succeed by raising a ton of money, then hiring a ton of people to realize the vision. In the Blub essay's terms, a modern company doesn't want to beat the average, they want to reliably hit the average with the 10s or 100s of people they're hiring.
This resembles the conditions at Google when Golang was conceived, a lot more than the "4 people want to max their individual leverages, whilst eating rice and beans to extend their runway" world the Blub essay was inspired by.
* Pick a testing framework / runner
* Learn a DSL to manage dependencies
* Learn how to configure the build system to ship static binaries
* Debate code formatting rules
* Build a CI pipeline to build and publish source code packages
* Build a CI pipeline to build and publish documentation packages
* Tune a GC for low-latency + low-memory
Rust often gets accused of being a "kitchen sink" language, but the Rust folks have tried doing the simple thing and it came with severe limitations - Rust 0.x was pretty much indistinguishable from a glorified Go. It even had green threads and GC!
It's no coincidence that they, much like Go, provide a test framework, build system, dependency management, CI, code formatting etc. out of the box. Because these things meaningfully improve productivity.
Rust is certainly a big language, but its features work together toward a coherent philosophy/paradigm, so it isn't really what I think of as a "kitchen-sink language". C++, Java, C#, etc have accumulated features from different languages and paradigms based on what was fashionable at the time rather than based on an overarching philosophy or paradigm.
That said, it isn't that Rust's "bigness" absolved it from limitations--it just opted for different limitations: notably a less productive programming model. That tradeoff makes sense in the context of Rust's charter--to be a maximize for safety and performance--but it's not an optimal tradeoff for general purpose software development (at least not as it exists today where productivity is king).
> It's no coincidence that they, much like Go, provide a test framework, build system, dependency management, CI, code formatting etc. out of the box. Because these things meaningfully improve productivity.
Agreed. I think Rust gets a lot of tooling and ecosystem stuff right (because these don't require it to compromise on its charter).
Git is great with this. You can get by just fine in 99% of cases with git add, commit, and push and those three could be explained even to a child. However, you can also fine-tune your pipeline just the way you like with Git's endless tweaks.
In fact, I claim that Paul Graham is completely wrong in that essay. To see why, think about Lisp and Haskell. When Lisp users look at Haskell, they know they're looking down, and they know why. "How can you get anything done in Haskell? It doesn't even have macros." But when Haskell users look at Lisp, they also know that they're looking down, and they know why. "How can you get anything done in Lisp? It doesn't even have a decent type system."
This situation - both languages certain that they're looking down when they look at each other - shows the problem in Graham's analysis. He assumes that languages can be placed on a one-dimensional axis labeled "power". They can't be, and the Lisp/Haskell issue proves it. That renders Graham's analysis invalid.
Instead, it might be better to think of languages as living in a multi-dimensional tree. Think of the program you're trying to write as having a vector in that multi-dimensional space. (Not so much the program itself, but what it makes it difficult to write.) Pick the language that extends the farthest in the direction of the vector defined by the program.
What Go did is recognize that "ability to get a large team of average programmers to maintain and extend a large codebase for decades" was one of the dimensions of the space. That makes it "Blub" by Graham's definition, but I don't think that's meaningful. Instead, if that's what your program needs, pick a language that does that.
The fallacy in that argument is that effective programming "in the large" requires more features, not less. Sure, there are highly dynamic programming languages that cannot meaningfully support programming beyond the scale of a "personal side project". Ironically for Paul Graham's "Blub" argument, LISP is quite clearly one such language. The same applies to languages such as Python, Ruby or ECMAscript, which would have been among the main alternatives to Go when it was first released.
And one can also fairly argue that Go has in fact made it simpler and easier to "program in the large" compared to C++, Java or C#, which were the other major alternatives to Go around that same time. So Pike's argument is not wrong as far as it goes. It is merely of limited applicability, because the jury is very much still out wrt. more recent and arguably higher-level languages like ReasonML, Kotlin, Dart, Erlang and yes, Rust. It's silly to conflate these languages with the likes of Python or C++, that is indeed the kind of argument that "Blub" was written for!
When compared to say, Ocaml Go definitely lacks some of the niceties of a type system (even now with generics). With ocaml i use recursion in almost every case i need to reduce / map something. Pattern matching + type constructors are VERY powerful and produces very safe code.
That said, i really like Go as a language. It lacks many higher level abstractions, and the coding style is very procedural. It's boring, and stops you from being to clever. Simply put, i get shit DONE in Go without much thinking. sometimes i work for hours in Ocaml to get a semi "easy" function to work correctly. With Go it's just write and things usually work.
Go has very good concurrency primitives for 95% of use cases. Ad hoc concurrency in Ocaml is still a pain (the new effect system WILL make this better) as you need to pick and choose between Ltw or async and then pray libraries support the one you chose.
For me Ocaml is still a language (that i love) for "being smart about it". And good ocaml code is truly beautiful and safe. Go on the other hand is about getting shit done. I can be productive in Go and then refactor later.
I really like that Go has such a good core that comes with the essentials, so you rarely need higher level dependencies (i would not write a databada driver myself).
Go sits in a spot that's well designed and has the right tools and brings good enough safety to the average app.
Unclear to me if this is really the meaningful reason that Paul's company succeeded (I'd say obsession with programming languages can be a counter-indicator for productivity), but it clearly resonated with a lot of people since that essay is arguably the onramp into Paul's fame. A lot of programmers read that essay back in 2001. His popularity as an investor who understood engineers arguably catapulted YCombinator
I also think the whole blub thing becomes less and less relevant with every passing day. Every language has evolved significantly over the last 20 years - when he wrote that article - much less the last 26 years since he started viaweb.
I personally prefer FP languages. I don't think they give me magical superpowers... okay, well, maybe BEAM languages in certain circumstances. But I also know people that work magic with C and do things I could never dream of.
I can't say the same for Java / C#, Rust / C++ etc ...
Personally, I think in abstractions. So a language that represents those directly is much easier for me to reason about than one that makes me constantly translate those low-level procedural code and back again.
Complexity can not generally be decomposed into smaller parts.
I think the reason Go gets a lot of criticism is that at a language level it misses a lot of constructs and features. So for everything it doesn't have, you'll find someone who is really used to leverage those constructs or features when they program and who favor that style, thus they will be bothered that Go doesn't have them, as it makes it less enjoyable for them to use Go.
Now normally this wouldn't be an issue, you'd say, just don't use Go. But here's the thing, the best part about Go isn't the language, but the compiler and runtime. That's the combination which attracts a lot of criticism, because everyone would like to be able to use the Go compiler and runtime since it creates beautifully efficient, small and low memory binaries with cross-compilation, and it has a good set of libraries.
I'd say on that front Go is unparalleled honestly, I can't think of any other compiler that can produce binaries for various targets that easily. Its cross-compilation is simply excellent.
That means everyone would want to use Go, but not because of the language, but because of the compiler and runtime. As all these people come to Go, they're forced to use the language, and very quickly realize this doesn't have the features or constructs that they'd want to program with, and there goes the criticism.
The trade-off is just hard to swallow, if you come from a more expressive and powerful language, with more features or styles, the Go language will be a harsh reality of how minimal it all is, so much so that they decide not to use Go.
So what's happening is a bunch of people are waiting for Go to have feature X,Y,Z before they jump back to Go, and that creates loud voices, and the reason is that they want to use Go the same way they currently program, but have access to its awesome compiler and runtime.
Zig, perhaps? My impression is that it's similarly capable.
Other than good cross-compilation, I really don’t see any of that. Even niche languages that have been around forever hit this exact trio like D. Also, the performance is ain’t that good to begin with, for more complex scenarios the comparatively dumb GC of Go will be a bottleneck, compared to something like Java.
Even if I ignore cross-compilation, what other language has easy and fast native compilation, that produces relatively performant and low memory programs, is properly garbage collected, and has a good library ecosystem as well as a good concurrency story?
This might have been a good tradeoff when they could barely fit a C compiler in a PDP-7, but that was half a century ago.
From https://groups.google.com/g/golang-nuts/c/7t-Q2vt60J8 the following:
Angle brackets require unbounded parser look-ahead or type information in certain situations (see the end of this e-mail for an example). This leaves us with parentheses and square brackets. Unadorned square brackets cause ambiguities in type declarations of arrays and slices, and to a lesser extent when parsing index expressions. Thus, early on in the design, we settled on parentheses as they seemed to provide a Go-like feel and appeared to have the fewest problems.
As it turned out, to make parentheses work well and for backward-compatibility, we had to introduce the type keyword in type parameter lists. Eventually, we found additional parsing ambiguities in parameter lists, composite literals, and embedded types which required more parentheses to resolve them. Still, we decided to proceed with parentheses in order to focus on the bigger design issues.
Also https://archive.ph/tZJbB archive of original proposal https://go.googlesource.com/proposal/+/refs/heads/master/des... which suggested using normal parentheses (!) for reasons: Why not use the syntax F<T> like C++ and Java?
When parsing code within a function, such as v := F<T>, at the point of seeing the < it's ambiguous whether we are seeing a type instantiation or an expression using the < operator. Resolving that requires effectively unbounded lookahead. In general we strive to keep the Go parser efficient.
Why not use the syntax F[T]?
When parsing a type declaration type A [T] int it's ambiguous whether this is a generic type defined (uselessly) as int or whether it is an array type with T elements. However, this could be addressed by requiring type A [type T] int for a generic type
Aside: it isn’t clear why not just make white space before the angle bracket significant - their examples of parsing problems are only if the code is not formatted “correctly” using gofmt. I think making the user type a space before greater-than would fix the parsing problem, and that seems like a reasonable compromise to reduce confusion for go vs most other languages. Square brackets usually mean indexing or arrays, and go breaks that pattern flummoxedly. F<T> versus f < t >
are syntactically different to read anyway ( whitespace is significant to pro grammers and writers for syntax ,for example).One could also argue that [] is the better choice precisely because it is similar to indexing a map - a generic type can be seen as a map of types to other types.
> Why not use the syntax F<T> like C++ and Java?
A great number of us are used to seeing <> rather than [] for generics.
It would seem limitations in Go's parser outrank a popular norm.
As an outsider it appears to me that the choice for square brackets has been made mostly for stylistic non-technical reasons, but perhaps politically that would be difficult to rationalise to the user-base! Making whitespace significant before comparison/shift operators (matching the gofmt layout) surely solves the stated practical issue of lookahead parsing; although I admit many language geeks would say significant space characters were an ugly solution.
To agree with you: there is a really good discussion on the downsides of <> here —https://lobste.rs/s/fmcviy/language_designers_stop_using_for — certainly square brackets are used for generics in other languages (Scala, Python, Nim, Eiffel).
Regardless, the point is moot, given that the syntax is now concrete!
> then by the same token <> is also easy to confuse with comparison operators
Not really, given that comparison/shift operators are not used in types, whereas [] is used in types. Perhaps go-lang wanted to reserve angle brackets for constraint operators a la Scala <: et al. </wink>.
Disclaimer: I have little experience with generics or language design. I am just very curious if there is a divergence between the stated reasons for the design, versus any unstated reasons.
That's not to say I'm not productive in go, but I could be vastly more productive.
I don't like that I'm not able to express simple constraints that I find a really important for improving code quality in other environments – things like "this field cannot be nil and it's a programming error if it is", or "all possible values of this field must be handled". There are lots of other bits, but I'd say the general pattern is being quite frustrated about having to do and remember things that the computer should be handling for me – such that working in Go always feels like having a small rock in my shoe.
It's a shame, because as a language—and wider ecosystem—it gets so much stuff really right.
These little warts that prevent that are everywhere: lack of sum types (no Option / Result), half-assed enums, no real ability to make things const, inability to express exhaustive switch statements, having to downcast to `interface{}/any`, nil, typed nil, and on and on and on.
The longer I develop software, the less I care about being able to write software that works and the more I care about being able to write software that doesn't fail. The former is table stakes, and I can throw together something that works in virtually any language. The latter is what not only prevents me from spending nights and weekends fixing problems, but also allows me to finish software and truly move on to new projects without having to constantly play whack-a-mole fixing new issues that keep coming up.
I coded primarily in Go at work for several years, although that was several years ago. I understand that package management and now generics have changed in major ways since then. But at the risk of being out of date, here are some language-level complaints I had at the time, focusing less on convenience/taste and more on things that I felt made it harder to write correct code:
- Nil pointers. Go doesn't have any built-in way of distinguishing between pointers that can be nil and pointers that can't.
- Two kinds of nil. Go distinguishes between typed and untyped nil pointers.
- Automatic zero-initialization of omitted fields. Adding a new field to a struct has a tendency to make existing callsites incorrect without producing any compiler errors.
- Mixing declaration and assignment on the left side of the := operator. Copy-pasting a line from an outer scope to an inner one silently changes assignments into shadowing declarations.
- Calling append() without capturing its return value is always wrong. This mistake is easy for linters to catch in simple cases, but aliasing creates more complicated cases.
- It's easy to make an implicit temporary copiy of an object without realizing it, like by using a value method receiver or by looping over a slice of values.
These are examples of things that made me feel like I was "fighting the language" to try to write correct code. I'd appreciate feedback about any of these that might have changed in the last few years.
But its effect is huge, e.g. the prototypical usage of the keyword would be to unlock mutexes, but it is simply a huge footgun, see: https://news.ycombinator.com/item?id=30253426
If it was scope-based, there would be just as many people complaining that it isn't function based.
They _should_ be identical, unless shouldDoSideEffect changes.
Yes, mutability is bad. One shouldn't change that. But avoiding "you're holding it wrong" footguns was the original goal, right?
Don't get me wrong, having both would be nice. Maybe something like `after` for function scope and `defer` for block scope. But that starts to erode the "simplicity" of Go, I guess.
if condition_A {
acquire_A()
defer release_A()
} else if condition_B() {
acquire_B()
defer release_B()
} else {
acquire_C()
defer release_C()
}
But with block-scoped defer you'd have to write something like: if condition_A {
acquire_A()
} else if condition_B {
acquire_B()
} else {
acquire_C()
}
defer func() {
if condition_A {
release_A()
} else if condition_B {
release_B()
} else {
release_C()
}
}()
Maybe the syntax here could be better with some kind of defer-if added to the language, but it's the non-locality of the cleanup that's the real downside. The whole point of the defer keyword is to let you put cleanup right next to resource acquisition, to make code easier to audit and maintain, and we lose that here. (Also this version might not even be correct, if the conditions change their values over time.)To be honest, I think Rust's combination of destructors and move semantics is really the ideal way of doing cleanup, with C++ coming pretty close too. But given a defer keyword, I can see situations where having it block-scoped would be painful.
Error handling is a big one for me. This could be generalized to lack of expressivity; it takes an unreasonably large amount of code to accomplish anything. Generics will help with the general case, but they don't do anything for error handling.
Lack of sum types, as you already mention, are another.
Go really likes forcing you to either write a ton of tests or discover your errors in production. More expressive features (sum types, etc) effectively mean that compiler provides those tests to me for free. _Can_ I write them myself? Yes, obviously, but that brings us right back to having to write way too much code to accomplish anything.
Farther down the list is escape analysis. Rather than just giving me control over whether allocation happens inline or indirect, if I have some performance critical submodule, I have to sit there and box with escape analysis; this is not only time consuming, but extremely brittle.
https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-... is a good read for fairly specific issues that _mostly_ haven't directly impacted me, but on the rare occasion they have, they've been infuriating.
this has been shown over and over to be incorrect. go programs are within the ballpark of other languages in terms of LOC. usually the examples given are niche use cases that impact an extremely small amount of code.
golang is looking for ways to make it more ergonomic just hasnt found a decent path forward yet. interestingly your sum types complaint might allow for it eventually.
> Go really likes forcing you to either write a ton of tests or discover your errors in production
this is just not true compared to most languages. evidence please. given the prevalence of dynamic languages in the wild today its most likely the opposite. i know my golang tests tend to be less in number than java or any dynamic language.
>Farther down the list is escape analysis. Rather than just giving me control over whether allocation happens inline or indirect
also not true, golang absolutely gives you the ability to control this. but its guarded by rules. rules that prevent you from screwing up and causing memory issues. this is a good thing.
Sum types may be coming in a close future release, since the notation
type T interface {
int | string
}
that can currently only be used for constraints on type parameters can be “easily” made to be a runtime thing as well. I cannot find the GitHub issue right now (perhaps, it wasn't an issue, but a comment in a very long thread, and GitHub is being annoying with those), but it seems like nobody is currently opposed to that, and the only reason it didn't make it into 1.18 is that it's already a pretty big language change. type Number interface { int | int64 | int32 }
var n Number
The default value for n has to be nil, rather than 0, because Go can't choose between the types (int 0, int64 0, etc.). And of course, you can't do n + x (assuming x is also a Number) because x might be a different kind of number. Still, it's a pretty good simplification from the current restrictions, and it builds well on existing concepts.I don't dislike Go though, and I still use it occasionally. I love the go formatter, and I like the built in channels (although now that I've experienced Elixir/Erlang I think the actor model is pretty great).
I think of it as a flavor of ice cream: some people will like it, others won't. Even if I hated Go, I still believe that the diversity in languages is a good thing and I have yet to see a language that didn't contribute to overall progress.
Yeah, Go is opinionated. I like functional programming style too (because I like being abstract), but it's hard for me to deny that it's easier for people to read code which is more concrete and standardized (fewer ways to express the same idea) even if there is more boilerplate.
> More than that, I hated having the language dictate to me where my code had to live (this has since been fixed). I also disliked the lack of a package manager (also since fixed).
These things have been fixed for a long time. It sounds like you tried Go when it was quite young.
> I think of it as a flavor of ice cream: some people will like it, others won't.
I agree. To expound on that, I'll also posit that people have different goals in choosing a programming language. Some people want a language that makes them feel clever / express themselves aesthetically and others want a ruggedly practical language / Get Shit Done (I definitely fall into both camps). Go is squarely in the latter camp.
> but it's hard for me to deny that it's easier for people to read code which is more concrete and standardized (fewer ways to express the same idea) even if there is more boilerplate.
Yes, absolutely agree. When working on a team, especially a big team, that's a huge benefit that shouldn't be overlooked.
Because I loved the language so much but I feel like it decided to be on the wrong level of abstraction, making many things, uselessly frustrating and verbose. What can be a very readable code in other languages can be hard to read in go because of its verbosity. And the lack of generic has been very limiting of a while, and many discussions with the main team turned to "what are your usecases that really need generic". So I felt like the language won't evolve the way I wanted it, the despite its redeeming qualities.
I suspect that a lot of people conflate "readable" and "terse" or "abstract". For example, beyond one or two chained methods, a foos.map(...).reduce(...) style quickly becomes unreadable while the equivalent for loop is still easily understood (this is a large part of the reason why complicated list comprehensions are discouraged in many parts of the Python world). It's also why it's often easier to understand error paths in Go than in a language which makes heavy use of exceptions despite differences in boilerplate.
For example, let's say our platform supports multiple authentication methods. If we need to add a new auth method, how do we know all the spots we need to update? TypeScript can handle this using a sum type:
type AuthMethods = "push" | "sms" | "fancy_new_thing"
// No errors
const authMethodHandlers: {[key in AuthMethods]: CallableFunction} = {
push: () => {},
sms: () => {},
fancy_new_thing: () => {},
}
// Error: Property 'fancy_new_thing' is missing
const authMethodTitles: {[key in AuthMethods]: string} = {
push: "Push",
sms: "SMS",
}
With the above approach, static analysis tells us when we missed a spot. That adds a ton of safety and expedites feature work (since engineers don't need to manually find all the spots that need updating).To me, Go is never the wrong choice, but it may not be the best choice for some projects.
People criticize that handling errors is manual, annoying and error prone.
People criticize that you can't implement your own or new data-structures generically, nobody wants to use a non-generic data-structure, and Go's own data-structures are generic, so people complained that users couldn't do the same.
People criticize that the lack of higher level list comprehension or stream-like API for data manipulation is tedious. Implementing a filter as a for loop every time gets repetitive.
People criticize that duck typed interfaces can make it hard to track what implements what, and if it makes logical sense or not.
Etc.
Those are things Java and Python programmers are used too for example, so it's not like the criticism is just from Haskell folks.
I don't think they necessarily need to go crazy in this regard(as simplicity is one of Go's best strengths), but I don't think it's elitism.
It predates Miranda and Haskell and is contemporaneous with ML, also developed at the University. [1]
ML had them too [2]
[0] https://en.wikipedia.org/wiki/Algebraic_data_type#Programmin...
[1] https://en.wikipedia.org/wiki/Hope_(programming_language)
[2] https://en.wikipedia.org/wiki/ML_(programming_language)
edit: add ML
> In any case, it's something I've been trying to figure out for a while and I don't think I have a complete explanation still. Curious to see what others think.
Couldn't the same be said for every programming language, ever? People often spend more energy complaining relative to the pain that they went through, I don't think there is anything specific about to Go about this. Just look at every discussion about C, C++, Java, JavaScript, Python, Ruby or even better, PHP.
I appreciate that the language designers had a specific vision for their language. I have much respect for them. They have a lot of experience and expertise. I prefer functional patterns so I decided to discontinue go and learn a different language.
there are not many 'better' languages to pick these days, all languages have pros and cons, even Rust won't save the world.
Personally, lack of generics never kept me out of Go. I did a lot of programming in it for about a year when 1.0 first dropped. I think it is a fine language, both with and without generics.
I do prefer simpler languages though - those that give me broad tools to accomplish many tasks rather than many tools to accomplish specific tasks.
This is a needlessly dismissive perspective. Put another way, now that one of golang's most prominent deficiencies has been addressed, people will switch their focus to other areas of frustration.
> Sometimes I think the core irritation with Go is its simplicity.
The core irritation (well, my core irritation) around Go is that this simplicity just kicks the cans of complexity and verbosity downstream onto its users, and—in the worst cases—hides the fact that this complexity exists, so encourages writing simple, obvious, and subtly incorrect solutions. Subtly incorrect is by far the worst kind of incorrect, as it results in code that works for all the obvious test cases but breaks at 2AM in production. Go doesn't feel simple to me. It feels half-assed.
Let's take a recent example I had to work with: truncating a string to the first 63 characters. Easy, right?
s = s[:63]
Simple, obvious, and subtly incorrect. Why? UTF-8. Strings in go are just a microscopic wrapper around immutable byte arrays. I could almost consider this okay if there was some type in the language that wrapped a byte array and an encoding to provide a real string type. But not only isn't one included, a reliable one can't even exist. The range keyword iterates over bytes for byte arrays and over UTF-8 code points for strings and there's no way to express anything different. String straddles this uncomfortable middle where it's both a byte array and a UTF-8 string and which one it is depends implicitly on which operators you use on it. Strings are "just" a byte array, except they're supposed to contain UTF-8, but not only is there no way to enforce that they do, the language makes it trivial to accidentally corrupt any UTF-8 string you do have (e.g., through indexing). If you're writing a function and somebody passes you a string you have no way to know or enforce that it's intended to bytes or ASCII or UTF-8 or any encoding whatsoever without iterating through to check. There is no type that actually encodes an enforced requirement of a valid UTF-8 string, nor can one even be written as a library.So the way you're "supposed" to truncate a string is:
s = string([]rune(s)[:63])
At least I think that's the magical incantation. Do you do this everywhere? Do your coworkers? Yeah, I didn't think so.Compare this to Rust and Ruby, which take two diametrically opposite (but both IMO reasonable) approaches. In Rust, strings are UTF-8. You cannot construct a String that does not contain a valid UTF-8 byte sequence (at least not without unsafe). You cannot perform safe operations on a string that result in an invalid byte sequence. If you are handed a string, you know that it contains valid UTF-8 bytes. You can iterate over either the bytes or the characters, but you have to explicitly decide which. In Ruby, strings are byte array / encoding pairs. It could be UTF-8, UTF-16, Shift-JIS, EBCDIC, or any other encoding you want it to be. When you're handed a string you can generally assume it's been parsed and validated under that character set. Indexing, iteration, and other operations are all encoding-aware. If you want the bytes, you can choose to access them.
The string equivalent in golang is, to put it bluntly, half-assed. It's mostly just a byte array but some keywords assume it's UTF-8. If you're given a string you generally assume it's UTF-8 but there's no reasonable expectation that it's valid since the language makes it ridiculously easy to violate the encoding rules. Go strings aren't even a decent building block for an encoding-aware type since range can't be made to work on other encodings. For a language written by none other than Rob Pike himself, I simply cannot fathom how golang arrived at this design.
Keep in mind this is just one of a myriad places where the language punts complexity to the user but, at best, doesn't give them the tools to actually reliably handle it and, at worst, pretends like the complexity doesn't even exist so it's not even obvious something needed special care in the first place. Simple, obvious, and subtly incorrect.
> When you're handed a string you can generally assume it's been parsed and validated under that character set
But it might not have been. It's a weakass guarantee, the same Go gives you, except in Go it's UTF-8-or-garbage, vs. Ruby's could-be-anything-and-could-still-be-garbage.
I still think that append() is the arch-example of that. I can't think of another high-level PL that doesn't have an atomic add-item-to-collection operation (either in the language or in stdlib) without requiring the user to coordinate all the moving parts.
Lacking generics is not in the same league as lacking sum types or other features. Only a few major static languages have sum types, whereas most major static languages have generics.
This Rob Pike quote gets bandied around in these discussions:
> The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
Ten years ago when I learned Go at the Recurse Center, I was early in my career and I picked Go as my language of study because I believed it was a good bet for my career prospects. I said to myself "I bet this is the next Java, and I bet that people who learned Java very early did very well for themselves". This bet proved correct; Go has been enormously useful to me in my career. Many people who go to RC pick languages like Haskell or a Lisp instead; things that are more interesting and challenging. While Haskell is well known for being mentally stimulating and very enriching, it is also well known for not seeing much industry adoption. You can sub a handful of other languages here, but basically just think of any very beautiful cutting edge experimental language that doesn't have a lot of industry jobs. I'm stick to Haskell for consistency in this post but you could just as easily say Lisp or Scala or arguably Rust these days; it's not about a specific language, it's about a specific grouping of languages that prioritize the beauty of the language and the effectiveness of the solo developer.
People get upset at Pike's framing that Go should be learnable by people with less-than-expert experience. As a person sees technology's ability to serve as a vehicle of class mobility, I see this as a great and noble design goal, because I think class mobility is fundamentally good. Ask yourself this: why do people consider it bad that a language acknowledge that it should help not only people with lots of experience, but also people with very little experience, and teams with mixed levels of experience?
My experience of programming Go for ten years and for being in these spaces for that time frame is that criticism of Go almost uniformly comes from people who can afford to invest heavily in things that have a low expected ROI; great and beautiful languages that don't have a lot of job prospects. Most people on Earth cannot afford the time commitment it takes to learn programming at all, let alone learning aspects of programming that they can't monetize.
I have never met a Go programmer that is bothered by the existence of Haskell. I have met many, many Haskell programmers that are bothered by the existence of Go. Ask yourself why that is. Ask yourself why someone would be so angry about something so wildly successful and useful to people. It's not even remotely a two-way street; the hatred goes one way.
This leaves us with the final problem: why can't people acknowledge that different people have different goals, and that those people's goals are just as legitimate as their own? Why are other people's goals threatening? It's because the existence of Go and the massive success of Go causes people to confront some aspect of class consciousness. It is impossible to witness the enormous success of Go and not confront the idea that programmers are laborers, and for some people, the idea of being a member of the labor class is simply not acceptable; criticizing Go thus becomes not merely an act of design criticism, but also as vehicle through which the critic can express something about their own socioeconomic class. There are many valid criticisms of Go, but there is also a category of Go criticism that exists more as a form of class signaling than anything else, and in these public forums, those two forms of criticism often get entangled in complex and opaque ways.
Because swaths of code written in a language where it's easier to make mistakes affects all parties and entrenches said language.
Imagine a book on programming an unreadable language like Befunge was given out to low income schools across the world, those people ended up organizing, and by sheer number and force cranked out software the industry depended upon.
Is it unethical to see the downside, point it out, but appreciate what was accomplished?
I think about the dilemma of socioeconomics preventing language choice from being a consideration and language mattering for writing better software a lot though.
This is easy.
Go programmers are more likely to claim language choice doesn't impact software much.
Haskell programmers are more likely to claim language choice does matter a large amount.
That means there's not as much reason for Go programmers to criticize other languages because they don't see it as mattering much.
I think "language doesn't matter" is a hypocritical position that people make in bad faith though.
That said, Go programmers definitely criticize other languages including Haskell for not being inferior to Go's brand of "simplicity".
Generics were the biggest feature keeping me from using the language, and now that's a done deal. I'm excited.
As a counterpoint, here's a survey that suggests that only 14.54% of developers have any interest in learning Go. How many of those who don't are turned off by lack of features? I don't know, but I'm not sure how you could either.
https://insights.stackoverflow.com/survey/2021#most-loved-dr...
If you wanted to be persuasive or helpful, you could try explaining what the other approaches to work around lacking generics might be, rather than just insulting people for being unable to figure out on their own how to avoid copy/paste spamming their codebase.
I could say the same about many of Go's critics, but I don't because that wouldn't be constructive. Rather than taking undue offense at my comment, why not articulate a counterexample to prove that there are valid reasons why a person (rather than a use case) might require generics?
> If you wanted to be persuasive or helpful, you could try explaining what the other approaches to work around lacking generics might be, rather than just insulting people for being unable to figure out on their own how to avoid copy/paste spamming their codebase.
Because these are already well-understood and have been debated to death. Instead of .map().filter().flat_map().reduce() you use a simple for loop. Instead of `LinkedList<ItemType>` you use `type List struct { Item ItemType; Next List }`. The thing that we generally aren't* agreed on is whether those extra characters are actually the end of the world versus the benefits associated with simpler, more concrete, more standard code.
Anyway, I wasn't "insulting" anyone, I was describing Go critics' objections in roughly their own terms (i.e., in so many conversations I've had with Go critics esp over generics, the conversation eventually terminates at some variation of "Go forces me to think about programming differently than I'm used to").
All three of these things are subjective.
> in so many conversations I've had with Go critics esp over generics, the conversation eventually terminates at some variation of "Go forces me to think about programming differently than I'm used to"
At some sufficiently large number of people, you need to acknowledge that how they think isn't wrong, the language is.
This justifies anything popular, including things that are now universally seen as bad, but once were popular.
I'm not even sure why I'm having to explain this. It's is a pretty obvious thing.
Not really. They aren’t formally defined, but there’s pretty wide consensus even among Go detractors about these qualities.
> At some sufficiently large number of people, you need to acknowledge that how they think isn't wrong, the language is.
Not when there are plenty of people willing to think differently and reap the benefits.
[citation needed]
> there are plenty of people willing to think differently and reap the benefits
"Reap the benefits" is, again, subjective. It's something you like, not something objectively beneficial or worth the trade-offs in all, most, or even necessarily many projects.
This is precisely the unduly arrogant attitude amongst Gophers that others have already mentioned several times in this thread.
> "Reap the benefits" is, again, subjective. It's something you like, not something objectively beneficial or worth the trade-offs in all, most, or even necessarily many projects.
Of course, productivity isn’t for everyone. Sometimes for personal projects I’ll opt for things that exercise my creativity and cleverness over the purely productive or pragmatic.
> This is precisely the unduly arrogant attitude amongst Gophers that others have already mentioned several times in this thread.
Hah! This made me laugh. Yes, I disagree with you therefore I must be arrogant. (:
Restated: You aren't arrogant because you disagree with me. You're arrogant because the things you say and the way you say them are arrogant.
Generics also aren't the only useful feature that Go lacks (or, as of today, lacked).
> Generics also aren't the only useful feature that Go lacks (or, as of today, lacked).
Agreed, but it's foolish to frame language debates purely around the presence or absence of useful features. Many languages have too many features including a great many misfeatures which degrades the overall experience, and your analysis doesn't account for that. Further, there are costs associated with things like generics which your analysis similarly ignores. Further still, it ignores things like tooling, ecosystem, learning curve, etc. This is how you end up with C++.
Performance only drives one of the two alternatives.
> Agreed, but it's foolish to frame language debates purely around the presence or absence of useful features. Many languages have too many features including a great many misfeatures which degrades the overall experience, and your analysis doesn't account for that. Further, there are costs associated with things like generics which your analysis similarly ignores. Further still, it ignores things like tooling, ecosystem, learning curve, etc. This is how you end up with C++.
This isn't an analysis. It's a counterpoint to a single individual on a social media website.
Go's ecosystem and learning curve are great. The best thing I can possibly say about its tooling is "it's better than C and C++". Seriously, how many dependency management paradigms have we gone through so far, and the best thing we can come up with is `go mod`?
Right, so use dynamic dispatch by default.
> Seriously, how many dependency management paradigms have we gone through so far, and the best thing we can come up with is `go mod`?
Agreed that the road to go mod has been tumultuous, but go mod is in the top tier of dependency managers, which is perhaps a sad indictment of dependency managers. Most languages still don’t offer reproducible builds, and several mainstream languages require that you generate your list of dependencies in an imperative DSL—or at least these build systems are the most popular, which brings us to another problem: some languages don’t even have a single standard tool! From dependency managers, let’s look at build tools: how many build completely static binaries by default? How many make you script the build in an imperative DSL? How many punt altogether? How many languages compile to native code in seconds or faster? How many languages can trivially cross-compile virtually any package? How many languages still make you spin up a CI job to build and publish source code packages? How many make you spin up a CI job to build and publish documentation packages? How many support testing out of the box? Benchmarking? Fuzzing? Formatting? Profiling?
As far as I can tell, Go trounces virtually every other language on tooling (Rust seems to do pretty well). One area where ago doesn’t do as well is debugging—I know Go has delve, but I understand it to be limited (haven’t tried it in a long time though to be honest).
I would like to understand a bit more about where a lot of the Go criticism comes from. Of course some amount of it comes from direct frustrations people have with the language design, but I suspect that doesn't account for all of it. It seems to me that the intensity with which some people fixated on the absence of generics cannot be explained just by frustration with writing non-generic code, which by all accounts was annoying but not overwhelmingly so.
So, for those of you who are willing to explore the part of this that goes beyond a simple rational analysis and criticism of language design, whats bugging you?
Sometimes I think the core irritation with Go is its simplicity. The suggestion that easy to learn languages can be effective too is somewhat humiliating, as we get attached to the pride of mastering more complex languages, and the suggestion is that some of the struggling we went through was unnecessary, and that folks who wont struggle as much might be able to be effective too, in fact the suggestion is that maybe our pride was misplaced.
Theres also a suggestion, in the fact that Go has opinions, that other opinions might be "wrong". I think this may bug people, who would take up those other opinions or see merit in them, a lot. There's also a bit of an anti-expertise "who are you, Go creators, to say you know better than me how to design this program/engage in software development?"
In any case, it's something I've been trying to figure out for a while and I don't think I have a complete explanation still. Curious to see what others think.