After I wrote my post on Python and asyncio, I had the opportunity to work with the Go language for some other projects, and I started jotting down my opinions on it as I did so.
After using it for a bit, I decided that it's mostly C with
concurrency, seamless M:N threading, garbage collection, fast
compilation, namespaces, multiple return values, packages, a mostly
sane build system, no C preprocessor, minimal object-oriented
support, interfaces, anonymous functions, and closures. Those aren't
trivialities; they're all rather great things. They're all missing in
C and C++ (for the most part - excluding that C++11 has started
incorporating some). They're all such common problems that nearly
every "practical" C/C++ project uses a lot of ad-hoc solutions sitting
both inside and outside the language - libraries, abuse of macros,
more extensive code generation, lots of tooling, and a whole lot of
"best practices" slavishly followed - to try to solve them. (No, I
don't want to hear about how this lack of very basic features is
actually a feature. No, I don't want to hear about how painstakingly
fucking around with pointers is the hairshirt that we all must wear if
we wish for our software to achieve a greater state of piety than is
accessible to high-level languages. No, I don't want to hear about
how $arbitrary_abstraction_level is the level that real
programmers work at, any programmer who works above that level is a
loser, and any programmer who works below that level might as well be
building toasters. Shut up.)
I'm a functional programming nerd. I just happen to also have a lot of experience being knee-deep in C and C++ code. I'm looking at Go from two perspectives: compared to C, and compared to any other programming language that might be used to solve similar problems.
It still has goto. This makes the electrical engineer in me happy.
Anyone who tells me I should write in a C-like language without goto
can go take a flying leap.
The concurrency support is excellent when compared to C and even compared to something like Python. The ability to seamlessly transition a block of code in between running sychronously and running asynchronously (by making it into a goroutine) is very helpful, and so is the fact that muxes these goroutines onto system threads more or less transparently.
Concurrency was made a central aim in this language. If you've not watched Rob Pike's Concurrency is not parallelism talk, go do it now. While I may not be a fan of the style of concurrency that it uses (based on CSP rather than the more Erlang-ian message passing), this is still a far superior style to the very popular concurrency paradigm of Concurrency Is Easy, We'll Just Ignore It Now and Duct-Tape the Support On Later, How Hard Could It Possibly Be. Why I went from Python to Go (and not node.js), in my opinion, is spot-on.
Many packages are available for it, and from all I've seen, they are sensible packages - not leftpad-style idiocy. I'm sure that if I look more carefully, a lot of packages mostly exist in order to patch over limitations in the language - but so far, I've yet to encounter a single 3rd-party uber-package that is effectively a requirement for doing any "real" work in the language, while the standard libraries don't look excessive either.
I don't exactly make it a secret that I am not a fan of object-oriented programming. I like that Go's support for OOP is rather minimal: it's just interfaces and some syntactic sugar around structs.
The syntax and typing are very familiar to anyone who has used C, and they seem to make it easy for editors/IDEs to integrate with (likely by design). It all feels very solid.
However, while defer, panic, and recover are an improvement over C, I'm less a fan of its oppositions to exceptions as a normal error-handling mechanism. Whatever the case, it was a conscious design decision, not an oversight; see Go's Error Handling is Elegant and Pike's Errors Are Values. The article Why should I have written ZeroMQ in C, not C++ (part I) also makes some good points on how exceptions can be problematic in systems programming.
My biggest complaint is that while I tend to prefer strongly-typed, statically-typed languages (and Go is both), I feel like the type system is also still very limited - particularly, things like the lack of any parametric polymorphism. I'd probably prefer something more like in Rust. I know this was largely intentional as well: Go was designed for people who don't want a more powerful type system, but do want types, and further, to support this kind of polymorphism involves tradeoffs it looks like they were avoiding, like those Russ Cox gives in The Generic Dilemma. (Later note: the Contracts - Draft Design proposal for Go 2 offers a possible approach for parametric polymorphism.)
My objections aren't unique. Ten Reasons Why I Don't Like Golang and Why Go Is Not Good have criticisms I can't really disagree with. (Also, did you know someone put together https://github.com/ksimka/go-is-not-good?)
All in all, though, Go is a procedural/imperative language with a lot of good design in language and tooling… which is great, if it's only procedural/imperative you need.