Go Is a Shop-built Jig (2014)
robnapier.net
robnapier.net
The implementation is a different story. It was really goofy to implement a new compiler backend, garbage collector, scheduler, calling conventions, and hey, why not make have the standard library make syscalls directly, what could possibly go wrong? The result has been the last several years has largely been spent reimplementing solved problems (basic compiler optimizations, a GC which is competitive with JVMs from 15 years ago, etc) rather than actually evolving the language.
In short, I wish they'd stuck with their "just do a few things well" philosophy in both language design and implementation.
Calling convention TBQH is something that often has to be custom in a language outside of interop bits, otherwise you are forced to deal with various C-style brammage. Unfortunately most OSes don't have language-independent interop calling conventions (Windows has COM, VMS has, iirc, SDL, the rest pretty much has nothing), so even if technically there's some official document practically you get stuck with peculiarities of a specific compiler and its relationship with C standard library. Syscalls are about the only safe interface on most unices and the reason why you can make self-standing Go programs that don't bring glibc garbage and random and unexpected shared library dependencies, something that is quite a reason for the following Go gets on container based platforms.
> a GC which is competitive with JVMs from 15 years ago
I don't know where you got this impression, but it's absolutely wrong. Go's garbage collector is excellent. It has sub-millisecond pauses [1] (for multi-gig heaps), and it mostly runs in parallel to the application.
You might be referring to Go's old GC, which was dropped when the current was introduced. [2]
[1] https://groups.google.com/forum/?fromgroups#!topic/golang-de...
GC (Go Compiler) has very peculiar characteristics: for one thing, it's written in Go itself. Another important thing is that it's very fast. That was an objective since the beginning of the Go project.
Finally, comparison with Java GC (garbage collection) are quite nonsense as Go has a very different allocation pattern than Java software.
I can always understand what a Go program is doing because of the simple semantics and lack of magic. It's an underrated benefit to be able to browse the source code of a dependency and grok it easily, first time.
if err = logit(FrobulatingMessage); err != nil {
return err
}
if err = f.cleanupOldest(); err != nil {
return err
}
There's an advantage, though. You can easily insert code for an error case. You might add, if cleanupOldest fails, something like logError("cleanupOldest failed unexpectedly while cleaning up chain")
if that's an unlikely but possible event that needs to be investigated. Big operational shops do that. It's hard to stick debug code in the middle of an expression.Before:
frobulate(message)?;
After: frobulate(message).map_err(|e| {
error!("error happened: {}", e);
e
})?;
// or, more like the go one
if let Err(e) = frobulate(message) {
error!("error: {}", e);
return Err(e);
}
It's really not that different in the debug case than go, but the normal case is less painful to deal with.(Emph. mine)
It hits the nail on the head: there's a certain tension between speed and correctness. For some, there's never enough time to do the right thing, but always (inevitably) enough time to keep fixing a wrong thing.
There are also diminishing returns of typing imperative code fast top to bottom, but having to deal with constant boilerplate and manually checking things that a machine could check, were the language more expressive.
Somebody called Go "systems PHP"; I think it's very fitting.
I think people coming to Go are coming from at least 3-4 different angles and it dictates their judgment.
There's people coming from languages that aren't statically type checked, from javascript to python. They like doing systems programming and having real concurrency. They like the plain style. They don't notice how weak Go's type system and abstraction faculties are, because it's already better than nothing.
There's people coming from C, who really like some things like easy concurrency, easy compiling, a package manager, and automatic pointer de-referencing, and (where applicable) GC.
There's people coming from C++ who will like the return to simplicity, but also hate it.
There's people coming from JVM and .NET who are just going to dislike almost everything about the language ergonomics of Go. You can get an actor model library and write those languages almost exactly like Go if you want to (without the bad syntax) but nobody does, because it's worse.
From my perspective, I wish people would stop complaining about Go's lack of generics. It's really an understatement. The real problem is Go basically doesn't have a type system.
Perhaps if we were to rebuild the ecosystems of each platform today things would look differently, but Java has so much baggage and shitty library APIs that it’s almost as if everything were written to abstract ideas with no regard for the human programmer using the final product. Even if your assertion that Go is the jankiest language after PHP aid correct, it basically doesn’t matter because it’s ecosystem is much more pleasant to work with than Java’s.
I don’t understand what “basically” means in this context. If you mean to say that Go’s type system isn’t formally sound, I think it would be better to argue that than to argue that it essentially has “no type system”. That strikes me as a very weird thing to say in general.
Same for Go. Built-in special case generic behavior -- but only for specially blessed types, and many other things besides:
https://medium.com/@tucnak/why-go-is-a-poorly-designed-langu...
The problem is, of course, that the scope grows, and with it, the need for more powerful tools. Go does not have C's macros (phew), but has nothing else either, so people end up writing code generators.
It’s not statically typed like Go but it’s far from inconsistent IMO
> Unlike (literally!) every other language with a similar operator, ?: is left associative. So this:
$arg = 'T';
$vehicle = ( ( $arg == 'B' ) ? 'bus' :
( $arg == 'A' ) ? 'airplane' :
( $arg == 'T' ) ? 'train' :
( $arg == 'C' ) ? 'car' :
( $arg == 'H' ) ? 'horse' :
'feet' );
echo $vehicle;
> prints horse.In what world is this sane language design?!
PHP is mad. I think they have to keep it for legacy reasons.
Don't get me wrong i think the way php handles requests is useful for the small websites i create and i would probably not use any other language for that. But PHP is THE dirtiest most inconsistent language that people actually use.
So I feel like there is this level of people who look at people who have to do PHP and actually enjoy it (and I have to say I am enjoying it, warts and all. Drupal is OK but where it really shines is https://laravel.com ) like we aren't 'real' programmers unless we somehow know its a terrible language and are only doing it because we have to.
I really like it.
With that said, if I'm being completely honest here, there are parts of the PHP community that just down right drive me nuts. There are a lot of scammy companies out there trying to sell PHP solutions that are such garbage. Its like JavaScript. PHP does have a low barrier to entry (but a high bar to being good at wielding it, much like JS. You could probably make this argument for a lot of web tech in general) that all kinds of people use it and really feels like it sullies the language quite a bit and some of the community around it.
Alas however, much like everyone hating jQuery without ever actually explaining themselves or what is the technical aspects of jQuery that make it so undesirable, PHP occupies the same space: everyone loves to hate it, it seems.
Go’s showstopper problem is dependency management. Fighting with Glide costs at least 30% of my time and every moment of it is excruciating. My organization started migrating to Dep just as Google announced its decision to have the Go community continue thrashing around on the issue even longer.
My advice: do not even begin to engage with the details of the langauge. That’s a waste of time. Understand that dependency management is horrifically broken and that Google considers it reasonable to thrash between experimental half-baked solutions forever. However much you may hate Java, Maven terminates with a correct output in less than an hour, which is more than I can say for anything in the Go ecosystem.
I'm not so sure if we really need full-scale generics like in Java or Rust, but the interface system (which is the Go solution for the Generics use-cases) certainly needs some improvements. For example, when you write a container lib which expects an empty interface as input you constantly have to cast the type while using the lib. The result is code which looks awful when you use the container and horrible when you look at the container implementation.
Next, using slices of interfaces is just another bad joke [1]. there might be technical reasons for the current state, but I am sure there are ways to solve those issues.
One thing I am not quite sure about the way Go handles bindings methods to interfaces. Currently, that is not supported and the practical way to do it anyway is to embed the interface into a struct and bind the methods to that struct, but that feels more like a hack than anything else.
Another thing that keeps bugging me is the requirement for map keys to be comparable while that is just some internal property which some types have and others don't. For example, functions are not comparable and therefore you can't use them as map keys. Finding out about such stuff really sucks when functions are considered first-class citizens.
That said, I have to add that I enjoy a lot of the positive sides of Go and while I wish those issue will be fixed someday, I will gladly continue to use it even with those issues.
Interfaces just represent a bunch of functions that an object must provide (and you can call when you "mask" the object behind the interface).
func (m myType) calcRandom() int {
return m.nine
}
But there are some situations when it would be useful to be able also bind functions to interfaces (e.g. some of them are similar to use-cases for abstract classes in OOP): func (i myInterface) calcRandom() int {
return i.Nine()
}
While I would consider it a clean language design to be able to do that too, it brings some additional complexity and as we know Golang doesn't like complexity.As I said, I am not completely sure of the consequences such a change would bring with it and if it would enable bad practices. So I would not push too hard for that one, but some of the other issues I mentioned are issues of which I am pretty sure that they should be fixed.
Interfaces are not the same thing as classes, and bringing anything from OOP to them will almost certainly result in Bad Things.
I feel like there's a physical workshop analog of how (Lisp) macros get you one and only one additional layer of abstraction. I've made an impromptu "table saw" by fastening an upside-down circular saw to a piece of plywood, but it certainly wasn't a basis for doing anything greater and I had to be real deliberate about plugging that thing in.
This is a straw man argument. How many people who complain about Go -- or evaluate it for a project, drop it and move on -- start out solving non-real problems?
How exactly would composition help you e.g. make a general purpose reusable math library for different kinds of numeric types?
A real problem is, “I need to draw this triangle”, and using math.Cos w/ float64s works just fine.
Not just math and scientific libraries. Even a simple reusable data structure. And fact it's so commonly needed for this, that Go's slices and maps would have been useless unless Go's designers have not cheated and used genericity for themselves...
And let's not get into reusable abstractions for behavior -- like a library that encapsulates the 5-10 more common uses for channels and goroutines (span N goroutines and wait for all to collect the results, span N and use first result, and so on).
Cmiiw even when have generic in place, we still need to write the code for each type right? Just the interface is the same. (this argument sounds silly actually, just want to say that)
This doesn't seem like it can be broadly true, though. Have you never needed containers besides Map and Array?
I've had similar experiences. I find that for some problems --- not all of them --- I can drill in quicker in Go, because the problem is all I can really think about; there isn't enough elbow room in the language to really spread out and work on the task of making things elegant.
At my last company, I found that writing an emulator in Go was super straightforward, and I think Go made that task easier than a "better" language like Clojure or Swift would have. But in addition to an emulator, we also wrote a compiler, and writing a compiler and SSA optimizer in Go was fairly unpleasant. Not prohibitively so, but enough that I noticed it.
If you’re really doing heavy list processing, transforming complex data structures, etc., then you’re probably going to miss map, reduce, and friends. But for most real world problems, the code is faster to write, faster to run, and easier to understand with a few dumb loops.
It’s always tempting to do things the clever way, but Go made me realize how rarely it’s actually beneficial.
Anyway, it hardly seems fair to give yourself choices (by introducing that “LlamaKit” library) and then criticizing Swift because there’s different ways to approach the problem.
(And I wonder if LlamaKit does anything besides obfuscate code? Hopefully I’m just missing some context.)
Wut? Firstly, no it's not. I have to drop into MinGW just to interface with native libs statically. Many languages don't do this. Secondly, C++ is very at home on Windows. Not a very good FFI story for something that is very cross-platform that it only binds w/ C libs with only one Unix compiler (gcc). It's like you got the phrasing exactly backwards and need to switch Go and C/C++.
> Go feels under-engineered because it only solves real problems.
Until I list the ones it doesn't. Or are they not "real"?
You know what? I'm not reading these things anymore. We know the type...supposedly senior devs writing a bunch of words about one language over another, cherry picking specific things for both to fit the narrative. I feel like this is how Michael Moore would write blog posts comparing languages. Sorry to hop on this article like this, saw it a couple of days ago with the Java/Kotlin blog post too and I'm seeing it every so often. And I like Go and Java. I'm afraid I just don't see these language opinions as that valuable anymore since I write in them all myself and know better.
Edit: In retrospect, probably a bit harsh. But I'd prefer the journalistic lang X does this but not this approach over the op-ed.
While I don't think he was trying to say Go solves every real problem (rather, just that the subset of problems Go solves are all practical and not theoretical), it would be constructive if you could take the time to list the problems you think Go is bad at solving, and why.
I'll probably agree, but I may also learn something, and I'll definitely learn more about where you're coming from.
One application I work on is a real-time query engine. Queries are parsed into ASTs, then reorganized into a higher-level graph reminiscent of relational algebra, then run through optimization stages, etc. Go is terrible at this in pretty much exactly the same way C is. Clearly, good software has been written in C that face the same challenges; PostgreSQL comes to mind, but I've read the PostgreSQL code, and while it's clean, I don't find it that readable or nice. Those guys work with C's limitations, of course, and that's fine.
One specific example here is matching structures. I look at Haskell and Scala with envy, because you can do things like this (pseudocode!):
match expr {
Or(p, And(q, r)) => And(Or(p, q), Or(p, r))
}
Similarly, writing graph-traversal (walking and transformation) logic in Go is painful. Go doesn't have sum types, so you have to dick around with interfaces (which weren't meant for this) and write big table-driven tests to ensure that all your matching cases is exhaustive.There's a lot of boilerplate, and there are times where I've been tempted to write code generators that use small DSLs that let me express everything in a higher-level way. But I've not taken that step yet.
If I'd started this particular project today, the choice would have been Rust or C++. This applies to future project that smell like something that'd need this kind of expressiveness.
Another area where Go disappoints is how surprisingly hard it is to write good concurrent programs that need to distribute work and handle errors in a resilient manner. Your actual logic — the meat of your program — quickly becomes obscured by error handling, ErrGroups, channel orchestration, inelegant wrapper structs, managing the lifecycle of resources (sockets and files and what not) and, ugh, contexts. Context is like a virus that morphs code into refactoring monsters every time you start weaving it through your application. I get Erlang envy when I need to do even the simplest thing, like coordinate the concurrent importing of chunks of data that is distributed to a dynamically growing/shrinking pool of worker goroutines. With all the bragging about modern CSP, Go provides nothing that can compete with Erlang supervisors, which is mightily strange for a (relatively speaking) new language supposedly invented for writing resilient networked systems.
I'm not sure it has much value to list places where Go may not solve the problem because we all know the limitations of our languages/platforms/runtimes. Among places Go is not the best: low-level embedded software, very performance critical software, software static correctness is more important than maintainability, etc. It's like saying Rust solves all real problems. While I guess mostly true, it doesn't mean too much to me. In general, I'm just finding these posts have little value to me anymore.
> Until I list the ones it doesn't. Or are they not "real"?
My reading of the original post is asserting that there is a specific set of problems that Go is designed to solve well, and that it's avoided providing functionality not related to those specific problems. The entire comparison is to a "shop-made jig", which explicitly solves only a specific subset of all problems you might encounter in woodworking, but is not at all a general-purpose tool.
This definitely fits with my experience with Go. If you want to build something that only needs concrete data types, channels, HTTP, and a database connection, and want to bang it out fast and move on, Go works well in this niche, because that's precisely the set of "real problems" that it was designed to solve.
I do not consider Go a general-purpose programming language. I would not use Go for ambitious large-scale projects. If I were putting together an engineering team to write a small network service, with junior developers, Go would be at the top of my list.
It shows even more in the fact that it originated, and afaik is still partially compatible, with a C compiler that is completely alien to both GCC and MSVC worlds. In essence, Cgo is a hack even on GCC.
Windows treatment of C and C++ as "just yet another language" is very alien, however, to the devs who come from unix world, and many basic things are not handled corrently by them or even planned for :(
The problem is that you can integrate other languages like C which often don't come with cross platform compilers. So any Go program which depends in some way on C is not cross platform anymore (as long as you don't have a cross platform C compiler).
To be honest I don't know how to solve that issue. On the one hand I really like the easy cross platform capabilities of the Go compiler, on the other hand I don't want to sacrifice language interoperability for cross platform builds.
I wonder how someone who has C/C++ experience thinks of Go as not being a cross platform language.
Support more compilers (e.g. clang, msvc). It can be more work than it's worth, sure and I understand the issues there, but jumping through hoops per platform (e.g. can't use a popular lib like https://github.com/mattn/go-sqlite3 on Windows without a MinGW gcc installation) is worth noting when touting the cross platform ease of use. Langs like Rust were lucky to compile to the same IR as a cross-platform C compiler.
> I wonder how someone who has C/C++ experience thinks of Go as not being a cross platform language.
I don't think that and definitely never would. I said it has issues, namely in the Cgo department. And I said that the other statement about C/C++ only being Unixy was wrong.
I definitely like Go and its cross-platform ability and hope I didn't give the impression that I don't.
Even then, it was a lot of work to be compatible with MSVC. Alex Crichton spent years on it. Not to mention the dozens of man-years of work that folks from Google put into getting it all to work on the clang/LLVM side.
Just to name one issue of many, MSVC uses Microsoft's PDB format for debugging instead of GCC's DWARF.
This has basically been the entirety of my experience with Scala. Having too many options - including just writing Java without semicolons - is ultimately not great for my own productivity.
The original title was: "Go feels under-engineered because it only solves real problems".
With something like C, C++, Lisp, Python or the like, you know that even if some large company abandons it, it probably won’t even make the headlines, since it won’t matter much for the rest of the users of the language – it has a large enough user base that the language will be fine.
But what if the controlling company decided to abandon Dart, Swift, C#, Go, etc.? It would kill the language in an instant. Or if they decided to simply take the language in a new direction more suited to the company but contrary to the wishes of its global user base? I can’t work under those conditions, and neither should anyone else accept to do so.
To clarify: If, say, at least two thirds majority of the development of the language was no longer done by a controlling company (and if the supposedly non-affiliated developers were not also all incidentally employed by the same company), and, in the case of Go, the name was changed, I would probably not have a problem with using any of the aforementioned languages.
P.S.
Rust is a slightly special case, since it’s dominated not by a company, but by the Mozilla Foundation, making it slightly more dependable than an arbitrary company.
I don't think Google is particularly the problem here. Go has lots of people working on and with it from outside Google.
The problem is that the core team is very resistant to any kind of outside ideas (while not being always brilliant with their ideas to compensate) -- and the community has fossilised to those loving the same ideas as well. It encourages a groupthink that's not there in other languages.
Contrast with e.g. Swift, which while run by Apple, has a great community participation story (to the point to going into too much bikeshedding).