Published at 09:32 on 21 June 2024
I just can’t seem to learn. I so much want there to be a better alternative (i.e. a good, modern programming language that compiles down to machine code) to C/C++ out there. And I just can’t stop thinking that Go might be it. Then I keep running into problems. Go (and its libraries) continually keep making it very difficult to do clever things.
My obstacle this time is how Go parses command-line arguments. Most modern languages do this by building a collection of objects to describe the allowed command-line syntax, making a parse call, and receiving in return a collection of objects describing the options and arguments found.
Go is different, probably because it was written by individuals skeptical of object-oriented programming. Instead, it is all based on passing pointers into an argument-parsing subsystem. When one initiates a parse, the pointed-to variables get set in ways reflecting what the user typed on the command line.
That might work well enough in the simple case, but my case is not so simple. I am trying to write a family of related commands, each with a set of standard arguments, and most with some custom, command-specific arguments as well. Moreover, there is a configuration file, and it is possible to get values from there if they are not specified on the command line with options.
In Java, I have done that by using the Apache Commons CLI library, subclassing the main class that holds the syntax description, and having it auto-populate itself with the standard arguments. Then my subcommands all use that class, and automagically get the standard options they need. No fuss, no muss, no repeated code, and all the options are in a single place.
Then I pull those parsed option values into the class representing a parsed configuration file, so that a command-line option will overwrite the in-memory copy of an in-file option. Presto! All the configurable values I need are now all in one place. And not only that, it was simple and easy to accomplish.
Go’s pointer-based argument parsing makes this basically impossible. Oh, there’s an alternate argument parsing library out there, but it is likewise broken by design, because it is pointer-based as well. Wait! There is a “value interface” that might offer an out? Nope, sorry, no escape: it only seems to support string values (even Boolean flags are unsupported!), and it is incompletely documented.
I keep running into this sort of crap with Go. And only with Go. Other modern languages just don’t seem to have this degree of pervasive brokenness. The Python standard library, for example, parses arguments much in the same way that Apache library does in Java. Even the hoary old cruft-fest of a programming language that is C++ has a popular third-party library that does the right thing.
It’s not just argument parsing. Previously, I struggled with how Go’s character set support can’t signal an explicit error condition when it encounters invalid input. Java, Python, Ruby, C++: all can do this if requested. Not Go, at least not out of the box and not without a lot of extra effort.
It’s bad enough to make me seriously question if there’s any problem space out there for which Go is the most appropriate solution. I know there are large, successful software systems written in Go, but my own personal experiences make me suspect strongly that those projects were much harder to get to their current state of completeness than if they had been written in some other, less limiting, less pervasively crippled by bad design, environment.
Perhaps it’s all just me, and others don’t feel the suck so much. Frankly, I don’t think so. But that’s just one more person. More damning, I think, is the verdict of Go’s original sponsor, Google. If Go really was the way to fill the need for a more modern language that compiles to machine code, then Google would not be sponsoring the Carbon project.
It’s all a shame, because to reiterate I really want there to be a better alternative to C/C++ out there. Alas, Go does not seem to be it.