Take Haskell. If I were a Haskell novice, and tried to do everything procedurally with do notation, and then complained that Haskell had all these problems, would that make Haskell a bad language? No. Would it mean that all Haskell programmers were telling themselves lies in order to keep using it? No. It would mean that I was using it badly.
If I were a brand new programmer, and took up Java, I might well complain about "public static void main(String[] args)". But an experienced programmer would brush that off, telling me that that's just syntax - there are real problems with Java, but that isn't one.
So this particular statement, that senior and more experienced devs ignore more and more problems, isn't proof of anything. It's particularly not proof that developers have to keep lying to themselves in order to keep using Go. (They may, but this isn't proof.)
I've been coding professionally since the 90s. The last project I worked on was in Go. It was pleasant and surprisingly productive. A few warts, like the parent said, but all told it was a great experience. I'm now working on a Typescript project. It is painful. Typescript itself is quite nice, but the deep problems with the rest of the ecosystem have not gone unnoticed.
I'd say if you run into someone who says they're experienced with X or love X etc., but they can't give criticism of it, they're most likely either not that experienced with X, or fanboys.
For example, I hate python as a language (spaces syntax? Self,self,self, lack of static type, etc) but I've been using it lately and it's nice using it due to libraries, community,etc.
Woe unto those who do not follow the idiosyncrasies of how golang handles versioning, package management, tooling etc.
if this is really your most important part of a language I would whole-heartedly recommend looking at rust which has been a breathe of fresh air in terms of package management and tooling.
Seriously though, Rust team needs to have its Come To Jesus moment about not piping shit off the internet to sh.
My company deploys many Rust (web) applications to production and I've not experienced these issues with cargo
To start off with, please read this: https://lwn.net/Articles/889924/
As you start having to worry about deploying to 10^5 scale machines you want your mindset to be more like the longtime kernel development folks. You want minimal dependencies. You want to build with minimal pulls to the internet. You want repeatable static binaries built from a source tree you can reasonably audit.
At that scale of deployment your "business risk" goes up significantly.
Rust's approach to tooling/packages looks very similar to the Node.js ecosystem. It's a total free for all and dependencies are managed really poorly (in the sense that they grow in number completely out of control).
I do wonder what the solution is though because Go has the same problem as far as I can tell. It has a bigger stdlib so there's less need to reach out. But when you do, you're in the same state with lots of tiny packages that do 1 thing well.
I don't have much experience outside of Go/Rust/Node so I can point to maybe a few languages which I can see being the opposite:
C++ has a large stdlib, and boost. With C++ managing dependencies is your problem so I guess it's easier to have few megapackages.
Python feels the same for mathematical computing. Numpy, TF, Pandas are all huge. Conda is an event bigger bundle, again I think because using the standard pip tools is rather cumbersome and usually not what people want to do
So as far as I can see, it's either easy to share code for others to use, in which case you get dependency hell, or you make it hard to share dependencies, but you have larger more refined ones as a result.
Hard to say what's better
And if you're still running something like CentOS 7, everyone just tells you to go download a Snap for it. eff that.
The fact that you can cross compile a win10 .exe on a raspberry pi with a single command as easy as "GOARCH=amd64 GOOS=win go build ." it's very powerful and I don't know a single language that does it out of the box like go does.
Go tooling is one of the strongest point of Go you have all of that built-in:
- compilation
- testing
- benchamark
- dep managment
zig build -Dtarget=x86_64-windows
I suppose you can define feature flags in your Cargo.toml but that's about it.
As far as I know, no other popular language allows you to read from multiple queues at the same time, which is an extremely useful pattern in concurrent programming. The only way to "select" from multiple sources in other languages is to use some kind of poll/select syscall on file descriptors - and even that is very limited, since kernel (at least Linux) does not allow userspace programs to create private file descriptors for intra-process communication - they must always be bound to some file/pipe/socket.
Off the top of my head, that's certainly possible in Rust, C++ and Java - probably many others I just don't know about.
That particular bit of concurrency goodness comes from the 70s, so you can be sure other languages picked it up.
But does that matter? In all my experience, I can't think of any instances where that wouldn't be a distinction without a difference. (Perhaps I'm missing something, though.)
Where languages differ is what they make easy to use, or first class.
As an example consider Go's autoformatter gofmt. It got released with the language, pushed and marketed as the way to format your code - first class. As a result, virtually all Go code is uniformly formatted. Contrast that with autoformatters in other languages pre-Go. They existed, but often multiple per language, each with hundreds of configuration options, all with minimal uptake in their ecosystems.
(If your point was about it being _built into the language_, then it definitely stands, see the other discussion in the parent comment thread)
I guess I just like it being built into the language. Plus no function coloring (everything is asynchronous, but appears synchronous) really makes it easy to build complex systems.
https://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tu...
(of course it was possible before)
This example sets up two timers which will regularly push events in the queue, while two threads processes these events. It's from before c++ had lambdas - nowadays it can be written much more tersely.
I'll try to write a short example after when I'm not on my phone - it should be reduceable to 5-6 lines of setup at most
#include <boost/asio.hpp>
#include <atomic>
#include <iostream>
#include <thread>
int main() {
boost::asio::io_context io;
using work_guard_type = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
work_guard_type work_guard(io.get_executor());
std::vector<std::jthread> t;
for(int i = 0; i < 10; i++)
t.emplace_back(std::jthread([&io] { io.run(); }));
std::atomic_int counter{};
while(counter < 100)
io.post([&] {
std::cerr << std::this_thread::get_id() << '\n';
counter++;
});
io.stop();
return 0;
}That's a trivial thing in javascript.
var a = new Queue();
var b = new Queue();
a.read(() => some callback);
b.read(() => some callback);
And code continues here without blocking.
To be honest, I think you could do the same in any (slightly) modern language.
Agreed, and things happening "at the same time" are possible only with parallelism. Hence my question =)
But sure you could also add parallelism using a few built-in Node modules (there's cluster and worker threads, also don't know if atomics are already in Node, they are in V8),
It's not trivial anymore but also not that hard. A newcomer could figure it out in ~30 mins from the docs.
During my PhD we had to crunch some numbers and my group usually makes use of C++ with libs for that. I tried doing it in Node using cluster and let it spread through our whole cluster of nodes (funny how it plays on words) and performance was worse obv. but not that bad, it was about 2x slower, but with that I was able to write code and test it in hours instead of days, and it didn't matter to me if a program that was going to take 2 hours was now taking 4 as I was going home anyway and check it out next morning ¯\_(ツ)_/¯. Plus, my experiment had a REST API for free (well, like 8 lines of code) and when I showed that to them they were :O.
I've written a fair amount of Go and I am not bothered at all by the general ergonomics of the language. Some of the issues the author points out (like accidentally copying structs with embedded synchronization primitives) are caught by linters which you should be using regardless of what language you are coding in.
Edit: I dabble with Rust from time to time as well.
Without that clarity, you end up with incompatible, spec-compliant implementations. This is a disaster, and so below a certain point relative to language complexity a shorter spec is simply wrong.
The Go team officially maintains two implementations, gc and gccgo, to ensure that implementation doesn't end up ruling over the specification as has happened in other languages. There are also other third party implementations, including tinygo.
Which spec-induced incompatibilities across these implementations are you referring to?
So the answer to your question is "not that I know of", but that's not evidence that a one-page spec is workable after all.
Depends on which implementation you're talking about. One of the stated goals of the Go project is that there must not be just one implementation. gc does not, but tinygo depends on llvm. gccgo depends on gcc.
I've been coding professionally since 1987, using everything from mainframe assembler, Fortran, C, VB, Java, C++, to most recently Go. IMHO, the language itself plays a smaller role in its usefulness than most think. As important is the tooling, stdlib, ecosystem, community and "StackOverflow"-ability.
Go has a few warts, like every language, but not that many, and they are more than made up for by how easy it is to assemble and run a project using it. It's that that makes it the most productive language I've used so far.