Why We Use Go
bravenewgeek.com
bravenewgeek.com
> The attitude of the decision making around the language is unfortunate, and I think Go could really take a page from Rust’s book with respect to its governance model.
(I wrote part of Rust's current governance model and was a part of polishing it up; though I wasn't so involved when Rust initially started using a consensus based model. I've also tried Go, and written about it [1])
... I don't know if this would work out so well. One of Go's strengths (also weaknesses, but it is a strength) is that the language designers can afford to just make hard decisions for everyone. This is where the simplicity comes from, IMO. Rust is complicated because it tries to cater to a wide range of needs. Go makes decisions even if they cut out use cases, and the language stays simple. Both approaches are valid; but IMO switching approaches would make Go lose one of its best plus points.
[1]: http://www.polyglotweekly.com/2015/04/24/thoughts-of-a-rusta...
For instance both had green threads. Rust designers ran into all the problems with them like interop with anything else and interrupts and fairness, so like everybody else that did M:N threading they threw up their hands and said "well it looked like a good idea at first" and removed it. Go designers literally called it a "rabbit hole" and followed it down to whatever crazy lands it lead to, because they promised people green threads so they had to keep it.
Edit: Is it about having stacks smaller than a page, maybe not aligned to 4K and not fighting for the same cache lines?
(Edit: I mean of course so we could allocate memory at their addresses for better locality, this is HN after all. Other ideas would need to be pulled to a different arena.)
However, the choices made by that library need not affect the language as a whole anymore.
I've written one largish (large for a 3 man team) production codebase in go (as well as numerous side projects). At first I loved it--it was simple, and I really loved the fact that it compiles to a native binary. I also really liked gofmt. But looking back through what the code has evolved into 2 years later, a huge portion of it is essentially copy and paste code to work around the lack of generics. Another large portion is copy and paste error handling.
I was essentially using go to fill several niches. I wanted a higher level language to handle business logic, but I also needed to work with serial ports, and I wanted something that would run fast with a small memory footprint.
Go seemed to fit the bill perfectly, but eventually go started to feel like jack of all trades master of non type of thing. Since I had to eventually rewrite my serial port code in C anyway, I think I would have been better choosing a language with more modern features for the rest of the project.
Maybe it's because I've been using F# (and C# to a lesser extent) lately, but I just don't think I want to go back.
- Its simple. Stupid simple. I can focus on writing code that is simple to grasp while following conventions.
- Its fast. Sure, its not the fastest language out there, but its hard to argue with its performance.
- Its feels like a lower level Python. Sometimes I think that's the main goal of the project.
- It comes from a respectable source. This means that it will probably exist in the next ten years.
- Setting it up is simple. No need to deal with runtimes like Java.
- Others who write Go seems to stick to the conventions. I rarely find the kind of surprises that you would find in C++ code. Since Go is simple to understand, people can write code that feels more "common". C++, well, its not fully understood by many (including me).
One point is that I don't use it to serve HTML or anything like that. Only to do systems programming. Servers talking to each other and/or responding to RPC or rest calls. Don't really serve anything other than files/json/xml.
I attempted to write the same service in Kotlin (http://kotlinlang.org), which felt mature and easy to understand. JetBrains did a great job also making it "boring." It felt like it was a boring attempt at making Java programming enjoyable. Along with 100% interoperable with Java libraries, alongside incredible tooling, made writing reasonably boring but functioning code very enjoyable.
To each their own, but I'm having a hard time justifying why I'd use something like Go over something like Kotlin. Maybe I'm missing something?
Performance aside which is a weak point of python, the language is certainly not inherently unmaintainable. My most maintainable and enjoyable projects are all in Python, you just have to not do stupid things which would make any project unmaintainable (like say no to spaghetti inheritance).
That's what they all say though. Low level, high level, static or dynamically typed -- it's not a problem as long as you don't do stupid stuff.
However, there are a lot of languages which encourage bad patterns (like mixing app and view logic, or implicitly convert arrays into numbers when comparing them to strings or something, he said, referring to no language in particular). Python is not one of those languages. Use it as a tool, not a toy, and it will easily help you produce very high quality, maintainable results.
Take a look at this for example, 1 year old hobby project of mine on which I still work daily - some of my most maintainable code. I have never, with any other language, experienced such joy at refactoring massive parts of a project given how easy it has been. https://github.com/jleclanche/fireplace
How many guns and how big are the guns that are pointed at my foot? Is it an ion cannon or an airsoft gun?
Most can agree one can be a happy user of $XLANG, but there are gradations of advantage depending on what you're working on.
Plenty of people out there that are happiest working in C, but I don't think many would say it's the safest or most productive language to work in.
Also worth thinking about how easy it is to extract meaning from software just by reading it. Explicit datatypes, specifications, grammars, etc. can all help with that. Even better is tools that can make sure your program faithfully represents an implementation of those specifications and act as a denser mental encoding of code than code itself is capable of being.
Easier said than done on large codebases with wide variety of contributors.
"Dynamic languages are fine as long as you don't do stupid things." One of the huge benefits of static types is that you can extricate yourself from those situations because the compiler can help guide you through large scale refactorings safely.
It's not even that the team is bad or that the authors of the code were ignorant - it may just be that the software has evolved to where the initial code no longer fits the problem exactly.
"Don't be stupid" is completely unactionable advice.
One reason dynamic typing has so much mindshare is because the comparison was always against Java which not only has some of the most boilerplate, but can't even prevent a null pointer exception. So in effect Java is 50% safer at 5x the cost of well designed Python program. However a language like Haskell actually brings real value from the type system (among other things), so you get more like 5x the maintainability at 50% the cost.
Obviously those are just made up numbers, and we can debate the particulars of Haskell vs other modern type systems, but it seems clear to me that is the richest soil for future gains in software maintainability and correctness.
Because intelligent programmers only use overly complex, obscure and hard to maintain (preferably functional) programming languages? I don't get it.
However, I think the author completely misses the power of composition. It's usually the best alternative if you want to share code between different types in Go. I have written many large go programs without ever using the empty interface or typecasting (except of numeric types, where sometimes you need it because go doesn't ever do implicit conversions between numeric types.)
I agree that the behavior of storing nil in an interface is a wart.
The author spends a long time arguing against the idea that the use of sync and atomic should be discouraged. He claims that it is hypocritical because the standard library uses these packages. But it doesn't seem hypocritical to me to put tricky optimizations into the standard library, but discourage ordinary programmers from writing tricky code in their ordinary applications. The standard library has millions of users; most code has a handful. The tradeoffs are going to be different.
Besides, the author is arguing against a strawman here. Go has always given programmers access to low-level programming resources such as inline assembly, operating-system-specific calls, and even the ability to directly make non-portable Linux system calls. Compare this to Java, where up until very recently, you couldn't even create a symlink without using JNI because the authors were afraid it might be non-portable.
I think micro-benchmarking the performance of channels misses a lot of the point. OK so you have a microbenchmark where mutexes are 30% faster than channels in a tight loop-- and you're willing to give up all the benefits of channels for that? Talk about missing the point. And if your goroutine does anything interesting with what it's pulling out of the channel, this is even more irrelevant.
Wanted to address this point separately. This sort of design is something you will find very often in well-designed video games: authors saying no to good features because they'll be misused and/or, on a grander scale, hurt the product.
I've enjoyed the little I played with Go. I can't comment on its simplicity/complexity nor the devs' design choices, but I don't think it's "toxic" as the author says, it's just a different way of developing a language. We need more projects doing this; I have rarely seen this be the downfall of any product - and when it is, it's usually because the design team is incompetent, which I definitely don't think the Go team is.
[1] Surviror bias note: we might mostly tend to compare newfangled languages to well-established languages, ignoring all the ones that have fallen by the wayside from our perspective.
Designers of competitive RTS games rely incredibly on player feedback. And they are eager to give it.
This ultimate simplicity, and encouraging programmers to write simple code (sometimes, I know, at the cost of having more tedious code) is not all bad.
The "dogma", and the "it's too complicated" parts of go, while sometimes are indeed a frustration. However, I feel like it has kept the language and the culture surrounding the language largely simple. So both a pro and a con in my opinion.
Having worked with go for several years, on many small project and a few large ones. The pros outweigh the cons.
One of the bigger ones for me is simply understanding other people's code rapidly. Go projects seem extraordinarily easy to understand without spending hours reading code. When I am in a position to support/operate/scale/troubleshoot other people's code, which is part of my role, this has been a less challenging experience than other languages.
Everyone has a cognitive limit on what they can keep track of/in working memory. Once your codebase exceeds that limit (in # of files, lines of codes, types, interfaces, etc.) you lose productivity. 10k lines of python is a lot harder to grasp than 10k lines of go.
And that's not even considering the ramp up time for someone new to the code to jump in, which is even worse in most dynamic language codebases.
Large codebases, ewww. Instead separate things into smaller projects, each easily maintainable on it's own.
It may be a great project for migration, just like Glance (although that one doesn't need that much performance).
The projects I don't think will ever be able to move are Neutron and Nova. Even parts of Nova are so tightly connected that it would need a major redesign before rpc can really split scheduler and other pieces into different projects. Also Keystone probably has too many integration layers / plugins by now to migrate to anything.
(I'm glad that this happened BTW, the more languages in OpenStack, the better isolated and simpler the interfaces will become)