Things That Make Me Sad in Go(lang)
Go is a pretty damn good language, and I've been really enjoying it while working on the newest iteration of Legend of Adventure. And while I've been positively titillated by the support for concurrency (and parallelism) from the get-go, it's got some rough edges that really rustle my jimmies.
My biggest complaint about Go is the way the compiler and other built-in tools expect you to lay your code out. Your repository is a package, and every subdirectory within it is a package. A package is like a module in Python.
The frustrating part is where your Go repositories end up. Normally, my work directory looks like this:
/opt/ bastascript/ blog/ crass/ oath/ otto/ panopticam/ sublime-btype/
And of course, each of the directories up there end up containing the source of their respective projects.
Go forces you to have something that looks like this (put on your imagination goggles and pretend that all of my projects are written in Go):
/opt/ src/ github.com/ mattbasta/ bastascript/ blog/ crass/ legend-of-adventure/ robertkrimen/ otto/
Let's be clear: I hate this. I hate everything about this. Nothing about this is good. Everything is bad.
- Fuck all that typing and autocompleting. That's four levels deep, compared to one.
- Don't remember who owns the project you want to work with? Oh well, go fuck yourself.
- Work with projects that are in languages other than Go? Let's have a look:
/opt/ bastascript/ blog/ crass/ oath/ src/ github.com/ mattbasta/ legend-of-adventure/ robertkrimen/ otto/ sublime-btype/
What a big mess.
To save myself at least a little bit of heartache, I just
ln -s /opt/src /opt so that I don't need to type
src/ every god damn time. But in all honesty, this is not good. Could I just soft link all of the project folders to their proper locations? No, I couldn't because I have more important fucking things to do with my life.
What an excellent segue into my next frustration:
Relative Package Imports
So let's say you have a project that looks like this:
aggregator/ importer.go downloader.go parser.go processor/ mapper.go reducer.go main.go
Your aggregator wants to map and reduce the data that it's aggregating. Great. What does the import look like?
And if you've got a project name like
legend-of-adventure, you're stuck doing this:
You can try
import "./server/regions", but don't worry: it doesn't work ("local import in non-local package"). Great, thanks Go.
The Big Community Mess
What the above leads to is virtually everyone trying to avoid using the vanilla Go systems in order to make anything work at all.
- Projects that require you to add them to your
GOPATHin order to compile. They do this so that packages can be imported without the project prefix.
- Projects that use a
make.gosetup to compile.
- Projects that change your
GOPATHto their own directory to compile.
- Projects that use a precarious dance of
go run'd scripts to get set up.
- Projects that actually use vanilla Go tools but rely on external packages that aren't using vanilla tools so you still need to do some git fiddling
I would hasten a guess that roughly three quarters of non-library Go projects use some unimaginably frustrating means of being installed. Because the vanilla tools have such an irritating, bad default behavior, the community has tried (unsuccessfully) to build their own solutions. And in doing so, everybody loses. Nothing about this has made anyone's day better.
Let's have a look at what the top community projects (that aren't libraries) do:
|Lime||Uses vanilla, but requires submodules|
|websocketd||Crazy custom makefile|
|nsq||Unclear, but it has one hell of a gnarly Makefile|
Compare this to...
- Node, where installing an application is just an
npm install -gaway
- Python, where
pip installdoes the job
Obvious Missing Features
- Deleting an item from a slice should require
arr = append(arr[:idx], arr[idx+1:]...). How is that friendly?
- There is absolutely zero implicit type casting. Comparison operators could benefit from this the most.
int? Can't compare them.
- Nested function syntax is very awkward. Why can't nested functions optionally use the same syntax as non-nested functions? We may never know.
- Integer math. All of the math functions (
floor, etc.) operate on
float64s. Go needs an
imathlibrary to do anything useful with integers at all. Presently, you need to write all of your own commonly used math functions yourself.
- There's a distinct lack of basic data structures.
set? Nope. Sugary queues or stacks? Nope. Ordered maps? Nope. Heaps? Nah. While of course it's possible to build all of these ourselves, it's not ideal. And frankly, the ones that are available as libraries in the community are mediocre at best.