I am interested in a language that makes things easy, clear and elegant. I agree with the original article in that I do not find Go to be any of these for many things I would use it for. Hence, I do not use it.
I think the author’s comment that Go ignored a lot of modern programming language theory especially hits it on the head for me.
Isn't the easiest way to establish that by writing code yourself rather than reading an opinion piece written by someone else who likely has a different coding style to yourself?
We built a heavily asynchronous back-end communications service in Go a year or two ago. It was a great learning experience. I learned that I don't like Go and its limitations and design choices first hand, wrote a bunch of notes about what I did and didn't like (mostly didn't like), and moved on.
We had chosen Go to use for the reasons others here have commented about (especially the part about enabling relatively inexperienced software engineers to be productive quickly), but in the end I felt it sacrificed too much on that altar and I would rather spend more time mentoring and training our software engineers to use tools I felt were better.
It's mostly a matter of opinion, like I said - with sufficient dedication you can pretty much do anything with any language. But I'm not investing any more time into Go when I have other tools out there (like Clojure, Rust, Haskell, Common Lisp, C#, various BEAM languages, Ruby/Python, etc.) that I find to be uniformly more pleasant to use.
A BEAM language would also be a reasonable choice, whether it is Erlang, Elixir, LFE or whatever.
People can (and have for decades) MCGyvered "cool projects" with all kinds of primitive and shitty languages and tools.
Progress doesn't come from projects, it comes from better tools.
As a caveman, you could build all kinds of heating projects, but you wouldn't get far if your only invention is the fire...
Some programming languages can be, and are, better than other programming languages. They've all been evolving along a single historical timeline, and many mistakes were made in the past that we acknowledge and wouldn't make again. To take a well-known example: eminently abusable Turing complete templates in C++ versus the more modern generics as seen in C#, Java, Rust, etc.
We should all have a shared goal of building of tooling that maximizes our leverage by providing powerful primitives, and minimizes the number of mistakes we make by providing dependable safety rails. Some more recent programming languages are really doing an exceptional job of this.
This critique of Go quite correct. It's a pretty good language with a lot of pretty good ideas, but it's also not without a healthy dose of very sizable flaws as well (which the author has enumerated nicely), and they're made more concerning because most of the Go team refuses to acknowledge many of them.
Without even going near the third rails of package management or generics, other niceties like real enums, ways to build typed non-slice non-map data structures, safety measures to protect against Goroutine leaks, or explicit interfaces would all be big improvements. I personally find that once it comes to large projects, Go feels obviously less brittle than Ruby/Python/JS, but much closer to their end of the spectrum compared to languages that offer stronger guarantees (e.g. Rust, Haskell, but also even languages like C#). I don't think that's what its creators were going for.
One feature which seems to lie along the axis of forward progress is lambdas (even Java got them) and one that appears to lie in its past is unstructured control flow (IMO this is somewhat regrettable). One thing I wish languages would all evolve is an expression-language syntax but there was and is a surprising amount of kvetching about Rust's "implicit returns" so perhaps I'll be waiting a while longer for that.
OTOH, the idea of forward evolution is also deceptive. The imperative world had tagged unions many decades ago, but they were abandoned in the OOP craze in favor of forms of subtyping (the Modula-3 designers at least gave this justification as a form of pure progress) and it's only fairly recently that they have percolated back over from the functional world and OOP-style subtyping has fallen from grace.
If you look at the people who have genuinely progressed technology, they spend more time inventing and writing code than soapboxing their opinions. They lead by example rather than simply talking about what they believe we should be doing. Those are far more valuable to the community than egotistical flame bait blog posts.
If everyone followed this approach, we would never have ended up with amazing languages like Rust. Why try to improve C++ if you can build cool stuff with it, right?
I use git as my dependency management. I pull a specific version of a golang dependency and put it into my private git repository. Then i don't have to worry about someone's github getting hacked, repositories getting deleted, etc...
Those problems have all happened with npm and github in the node ecosystem. Go solves these problems by allowing you to decide on how to manage your dependencies.
I assume NPM has gotten a lot better in the last few years if it's being used as example of something to strive towards.
Every time I have to mess with the Node ecosystem, it really makes me question the direction I'm heading with the research or project I'm working on, just by the haphazard way modules seem to be used and dealt with on both the dependency level and at the repository/packager level.
The last time was a couple months back with Reasonml. Reason seemed (and still seems) really cool, but ultimately I wasn't willing to put up with the umpteen layers of indirection just to compile to JS.[1]
1: I know there's an Ocaml compiler as well, but it didn't easily support some of the use cases I was looking at.
Make sure that you are evaluating the tool not how the community uses the tool or npmjs.com runs the registry.
And not mentioning the security issues associated with having a central repository for packages.
> deploying dependencies in those languages is really painful.
Can you please elaborate here? What's painful about Rust dependencies?
I don't know much about Glide but if it uses Git for packages then that's pretty much centralized thanks to GitHub, too. At least the properly centralized solutions help assure you that, unless you're running a server yourself, the package is guaranteed to exist online, and even the developer can't remove it. On GitHub there's far too many things that could go wrong, and I have much less confidence that repos will continue to exist.
What approach would that be?
> Why try to improve C++ if you can build cool stuff with it, right?
Go improves on C++ in at least one regard, compile time. And there they succeeded spectacularly. While I like Rust, imagine how great it would be with Go's compile speed..
Disregarding all critique of a language as "personal opinion" as long as you can "build cool stuff" with it.
> Go improves on C++ in at least one regard
Go is a GCed language, so you can't really compare it to C++.
GP said it's more valuable to evaluate a programming language by the projects people make with it, than by reading the 417th piece of perceived strengths and weaknesses of it. I think GP has a point.
> Go is a GCed language, so you can't really compare it to C++.
That's a very strange thing to say.
And I don't, so let's just agree to disagree on that.
> That's a very strange thing to say.
Does this (or any other) advantage of Go help people who are writing software that cannot have a GC in the loop? No, it does not, so it's really not a valid comparison imo.
Could Rust and C++ have better compile times? Maybe. I'll defer judgement to the actual compiler experts though.
Go has a GC because its creators thought they needed one to support concurrency.
This is also a very strange rationale, for several reasons.
1. You can program in Go without a GC
2. There are no applications that prohibit GCs; there are _some_ which prohibit long and/or nondeterministic pause times
3. Even if you were right about 1 and 2, these still wouldn't be reasons for not comparing Go and C++. In particular, your argument is in the form: "You can't compare Go and C++ because {comparison between Go and C++}".
But Go was designed to be used with a GC, no? I see the same argument used with D, even though a lot of the stdlib relies on a GC :P
> 2. There are no applications that prohibit GCs
You are making a very strong statement here. Since we're on HN, I'll give you the benefit of the doubt.
I am not a GC expert, so I have a follow-up question: does a deterministic GC exist? If so, what kind of pause time bounds (error/variance) are we talking about? Depending on the answer to this, I could probably list several areas where the bounds are unacceptable.
> 3. Even if you were right about 1 and 2, these still wouldn't be reasons for not comparing Go and C++.
Of course you can compare the two languages, but my argument is that it's essentially a useless comparison.
I'm not sure this question makes sense. It was designed for easy memory management for the default case by way of GC, but it was also designed to allow users to opt into their own memory management schemes for niche cases. In any case, this seems even less relevant than your original claim.
> You are making a very strong statement here. Since we're on HN, I'll give you the benefit of the doubt.
It's a strong statement in that it uses absolutes, but it's pretty obvious. "No GC" is not a requirement, it's an implementation decision that assumes GCs are incompatible with the actual requirements (usually something like, 'there may not be unexpected, long pauses' for some values of 'long' and 'unexpected'). In many such cases (for example, game development), Go's low-latency GC is perfectly suitable. In other cases, you can just mind your allocations.
> Of course you can compare the two languages, but my argument is that it's essentially a useless comparison.
I disagree. To pick a language to use for a new project, you're best served by comparing the candidate languages (as opposed to picking one at random).
I can't tell if you're arguing that all Turing complete languages are equally useful, or that "utility" is a bad metric for evaluating programming languages. In the first case, of course this is false or Docker would be written in BrainFuck. In the second case, you've given no alternative (maybe you rank languages by the coolness of the type system?) nor given any justification for why 'utility' is a bad metric.
I'm not sure why you think my argument is constrained to these two choices?
I never said utility is bad. My argument is that critique of a language can be constructive even if the language has utility. In other words, I am against dismissing critique of a language simply because the language has utility.
The OP said he didn't weight intellectual criticisms more highly than observations, which is a pretty reasonable position. I'll go a bit further and say that criticisms based on theories aren't worth very much when the belying theories do a bad job of predicting the observations. For example, if the theory is "Powerful type systems predict language utility" and the observation is "Very little software is built with languages with powerful type systems, especially very little _important_ software", then criticisms based on that theory aren't worth very much.
Are you even reading my comments?
> I'll go a bit further and say that criticisms based on theories aren't worth very much when the belying theories do a bad job of predicting the observations.
True, but only if the observations you make include both direct and indirect impacts.
Following your example, it is true that very little software uses type system-oriented languages, but you are ignoring the impact such languages have made on other languages, tools, frameworks, and libraries.
For instance, Haskell is not a widely used language, but some of its ideas have impacted other languages and domains in various ways.
Of course. I'm referencing them. Perhaps you mean something other than you're saying?
> True, but only if the observations you make include both direct and indirect impacts. Following your example, it is true that very little software uses type system-oriented languages, but you are ignoring the impact such languages have made on other languages, tools, frameworks, and libraries. For instance, Haskell is not a widely used language, but some of its ideas have impacted other languages and domains in various ways.
No, you've confused yourself by adding in Haskell. The theory in your example is "type system-oriented languages predict utility", it has no indirect effects, it has direct effects on Haskell and the languages that Haskell passed this trait onto (and other languages that aren't inspired by Haskell).
When C++ modules finally get integrated in C++20, that advantage will fade away.
The fact this is argued as a bad thing is personal opinion. He might have a valid point on that specific arguement but it's still an opinion piece as there have been others who have voiced how they like vendoring in Go.
> If everyone followed this approach, we would never have ended up with amazing languages like Rust. Why try to improve C++ if you can build cool stuff with it, right?
The guy isn't designing a new language or trying to improve upon an existing language. He's just airing his opinion.
I see language bashing really as more of a tribal ritrial rather than a productive contribution. Like the KDE Vs GNOME wars or the vi Vs emacs debates before that.
For what it's worth, when I haven't liked a particular language which I've depended on, I've written my own parsers to add features. I'm not suggesting everyone should do this but it's a great deal more useful than starting flamewars.
So how do you think new language development starts? Why do we have languages like Rust, D, Nim, and Crystal?
By people writing code rather than pontificating
> Why do we have languages like Rust, D, Nim, and Crystal?
Because people wrote them rather than wasted hours on end arging why other popular solutions are garbage. As I said in my previous post:
"For what it's worth, when I haven't liked a particular language which I've depended on, I've written my own parsers to add features. I'm not suggesting everyone should do this but it's a great deal more useful than starting flamewars."
Or to put things another way, you don't see the developer of Nim endlessly slagging off other languages. He just quietly gets on and creates something awesome.
Or maybe a both things are important?
Vendoring solves sharing dependencies with your team, it does not solve the addition of new dependencies. You still need to get your dependencies somehow, and they need to be able to specify what dependencies they work with. And your libraries can't vendor their libraries in go because then they'll have a different import path in vendor directory and in the main project and you won't be able to share types.
Much of the problems w/ dependency management have to do with over-coupling and adding lots of random dependencies for things you could just inline into your project. Also why the stdlib is so strong.
2. You can pick specific versions and follow basic semver constraints if you want by checking out the source for the dependency locally, and then copying to vendor. Automatic resolution would be nice but it's not like you can't do it.
3. Better automatic dependency management is coming. But honestly I prefer it to be arduous to add an external dependency. You should only do it if you absolutely need to.
The bit about import paths being wrong I've generally not found to be the case; the go compiler will treat deps with the same in vendor or in gopath.
IMHO it should allow me to either use a shallow clone or use crates.io's API directly.
If everyone followed this approach, we would never have ended up with amazing languages like Rust.
This comment reminds me of the top comment in a prior anti-Go spiel (which of course did well on here).
https://news.ycombinator.com/item?id=7660924
Is it interesting how little of consequence is written in Rust, or in that example Haskell, and how much is written in Go, C, and even derelict dregs like PHP. The formal perfection of a language does seem to have a great deviance from the likelihood that it serves as a basis for important work.
Exactly this. It always amazes me when people respond with "You can't judge a language by the things that are built with it because you can build anything in any Turing complete language". They're pretty much arguing that haskell/rust etc are better than go because all languages are equal.
That is pretty simple to explain. Well designed languages have a learning curve. They require an initial investment in terms of learning — something that a lot of self styled hackers are not willing to put in. I am sure a ton of stuff is written as shell scripts.
And there is also the matter of how long the language has existed.
Edit: You seem to be overlooking the less tangible contributions languages like Rust or Haskell have made to PLs in general. One example that comes to mind is how Scala has affected the development of Java, even though, in the real world, Scala use is relatively non-existent.
Seeing these for Go, Rust, Elixir, Elm, and others let me narrow it down before spending the 4-8 hours to figure out if it works for my brain and use case. (For example, seeing this tells me that Go is bad for the things I want to build because my brain works in types and good error checking.)
I'd prefer more of these, frankly.