Adam Leventhal's blog

Close this search box.

I Love Go; I Hate Go

August 2, 2016

I liked Go right away. It was close enough to C and Java to be instantly familiar, the examples and tutorials were straightforward, and I was quickly writing real code. I’ve wanted to learn Go since its popularity was surging few years ago. In no danger of being judged an early adopter, I happily found a great project that—as it happened—had to be in Go (more in a future post).

I Love Go

My first priority was not looking stupid. The folks I’d be doing this for are actual Go developers; I wanted my code to fit in without imposing with too many questions. They had no style guide. Knowing that my 80-column width sensibilities expose an unattractive nostalgia, I went looking for a max line length. There wasn’t one. Then I discovered gofmt, the simple tool that Go employs to liberate developers from the tyranny of stylistic choice. It takes Go code and spits it back out in the One True Style. I love it. I was raised in an engineering culture with an exacting style guide, but any style guide has gaps. Factions form; style-originalists face off against those who view (incorrectly!) the guide as a living document. I updated my decades-old .vimrc to gofmt on save. These Go tyrants were feeling like my kind of tyrant.

One of the things that turned me off of C++ (98, 11, and 14) is its increasing amount of magic. Go is comparatively straightforward. When I reach for something I find that it’s where I expect it to be. Libraries and examples aren’t mysterious. Error messages are non-mysterious (other than quickly resolved confusion about methods with “pointer receivers”). Contrast this with Rust whose errors read like mind-bindingly inscrutable tax forms.

Go’s containment-based inheritance is easy enough to reason about. Its interfaces are similarly no-nonsense and pragmatic. You don’t have to define a structure as implementing an interface. You can use an interface to describe anything that implements it. Simple. This is particularly useful for testing. In a messy environment with components beyond your control you can apply interfaces to other people’s code, and then mock it out.

The toolchain is, again, simple to use—the benefit of starting from scratch—and makes for fast compilation, quick testing, and easy integration with a good-sized ecosystem. I stopped worrying about dependencies, rebuilding, etc. knowing that go run would find errors wherever I had introduced them and do so remarkably quickly.

I Hate Go

Go is opinionated. Most successful products have that strong sense of what they are and what they aren’t; Go draws as sharp a line as any language. I was seduced by its rightheadedness around style, but with anything or anyone that opinionated you’ll find some of those opinions weird and others simply off-putting.

Reading the official documentation, I found myself in the middle of a section prefixed with the phrase “if GOOS is set to plan9”. Wow. I’m a few standard deviations from the norm in terms of being an OS nerd, but I’ve never even seen Plan 9. I knew that the Plan 9 folks got the band back together to create Go; it’s great that their pop audiences don’t dissuade them from playing off their old B-sides. Quirky, but there’s nothing wrong with that.

I wanted to check an invariant; how does Go do assertions? Fuck you, you’re a bad programmer. That’s how. The Go authors feel so strongly about asserts being typically misused that they refuse to provide them. So folks use one (or more) of some workable libraries.

I created an infinite recursion, overflowing the stack. Go produces the first 100 stack frames and that’s it. Maybe you can change that, but I couldn’t figure out how. (“go stackoverflow” is about the most useless thing you can search for; chapeau, Go and Stackoverflow respectively.) I could be convinced that I only want 100 stack frames, but not the last 100, not the same 4 over and over again. I ended up limiting the stack size with runtime.debug.SetMaxStack(), Goldilocks-ing it between too big to catch the relevant frames and too small to allow for normal operation.

I tried using other tools (ahem, DTrace) to print the stack, but, of course, the Go compiler omits frame pointers rendering the stacks unobservable to debuggers. Ditto arguments due to ABI-non-compliant calling conventions, but that’s an aside. The environment variable GOEXPERIMENT=framepointer is supposed to compile with frame pointers, but it was a challenge to rebuild the world. All paths seemed to lead me to my former-colleague’s scathing synopsis: Golang is Trash.

As fun as it is to write code in Go, debugging in Go is no fun at all. I may well just be ignorant of the right tooling. But there sure isn’t a debugger with the simple charm of go build for compilation, go test for testing, or go run for execution.

Immaturity and Promise

Have you ever been in a relationship where minor disagreements immediately escalated to “maybe we should break up?” The Go documentation even seems ready to push you to some other language at the slightest affront. Could I have asserts? Sure, if you’re a bad programmer. Perhaps ABI compliance has its merits? I’m sure you could find that in some other language. Could you give me the absolute value of this int? Is something wrong with your ‘less than’ key?

I’m sure time will, as it tends to, bring pragmatism. I appreciate that Go does have strong opinions (it’s just hard to remember that when I disagree with them). Weak opinions are what turn languages into unreadable mishmashes of overlapping mechanism. My favorite example of this is Perl. My first real programming job was in Perl. I was the most avid reader teenage of the Perl llama and camel books. Ask me about my chat with Larry Wall, Perl’s creator, if you see a beer in my hand. In an interview, Larry said, “In Perl 6, we decided it would be better to fix the language than fix the user”. Contrast this with Go’s statement on assertions:

Go doesn’t provide assertions. They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting.

Perl wants to be whatever the user wants to be. The consumate pleaser. Go sees the user as flawed and the strictures of the language as the cure. It’s an authoritarian, steadfast in its ideals, yet too sensitive to find compromise (sound like anyone we all know?).

Despite my frustrations I really enjoy writing code in Go. It’s clean and supported by a great community and ecosystem. I’m particularly heartened that Go 1.7 will compile with frame pointers by default. Diagnosing certain types of bugs is still a pain in the neck, but I’m sure it will improve and I’ll see where I can pitch in.

23 Responses

  1. “Weak opinions are what turn languages into unreadable mishmashes of overlapping mechanism. My favorite example of this is Perl.”

    That’s why I love writing software in AWK: small, runs like a bandit, and if that time where I need that extra burst of speed ever comes, there is always libawka, to turn it into ANSI C and compile it with an optimzing compiler.

    The thing I like best about AWK: there is one and only one way to do something. As far as language constructs and syntax is concerned, AWK is so small and so clearly defined, that it’s the antithesis of Perl.

    1. Thats what I really enjoy using Go. I love the concepts you mentioned above, small, clearly defined as well as apinionated. Currently trying to bring those concepts to the Web I’m involved in the development of which is similar to Go including CSP, but runs in the browsers

    2. I love awk too, but we’re dating ourselves 😉

      If you look at DTrace syntax you’ll see clear awk inspiration.

      1. I did, and I do, and it’s the greatest thing since sliced bread. I was able to immediately grok DTrace because you decided to use AWK-like constructs.

        And we should not feel bad about “dating ourselves”: people get involved in the technology details and tend to very quickly forget that we use computers to process data quickly and efficiently, and to automate work. So if a language like AWK gets the data processing, or some process automation done, and done well, then the core purpose of using computers has been addressed.

        Don’t feel bad about it – teach.

        I have zero remorse for using “old” technology, especially if that “old” technology is lightyears ahead of what we have today in terms of simplicity, resource usage, and speed.

        After all, it is not our fault that today’s contemporary computer people did not know about things like AWK, but I for one am doing something about it, I teach it to my apprentices. In my view, if something is good and useful, it is timeless.

  2. Perl 6 learned from Ruby apparently because Ruby always was as flexible as one could be. While being syntactically prettier than perl.

    Your statement about perl is unfair though:

    “Weak opinions are what turn languages into unreadable mishmashes of overlapping mechanism. My favorite example of this is Perl.”

    A lot of this follows from “There is more than one way to solve any given problem at hand.”

    This means more complexity, true. The alternative is to try and go the python way “one size fits everyone”. And even the python guys fail at providing only one way – the best example is biopython. They have a section in their official tutorial that there are sometimes more than one way to solve something, so even the python guys acknowledge it:

    “2.1 General overview of what Biopython provides”

    “[…] One thing to note about Biopython is that it often provides multiple ways of “doing the same thing.” Things have improved in recent releases, but this can still be frustrating as in Python there should ideally be one right way to do something. […] “

  3. A competent debugger/tracer can use stack unwinding to generate backtraces, rather than force wasting of a register on the task. Maybe GO/LLVM don’t produce that proper unwind data, but dtrace also deserves blame for not consuming it.

    1. Sort of… if you explore how DTrace works you’ll see how tricky that would be. DTrace relies on being able to take a stack from arbitrary context, so, for example, you can’t do file io to dig out DWARF or the like.

  4. Have you tried D already (e. g.
    I underwent a similar journey as you and finally found D to be the least shitty language out there 😉

    1. D is really fantastic… I’m looking forward to the GC and performnce improvements coming. However, I can’t make an argument yet to using it at work until basic ecosystem libraries like aws-sdk arrives.

      1. Rust is also looking great, although more complex than Go with its lifetime types. Same thing applies, we need more community library support before mainstream adoption.

  5. Minor typos:

    “They had no a style guide” -> “They had no style guide”

    “The simple tool that Go employs to liberates developers ” -> “The simple tool that Go employs to liberate developers ”

    “Factions forms;” -> “Factions form;”

    “its great that their pop audiences ” -> “it’s great that their pop audiences ”

    ” So folks us one (or more) of some workable libraries” -> ???

    “rendering the stacks unobservable to debugger” -> “rendering the stacks unobservable to a debugger”

  6. I hate assertions, easily find the abuse of using it. Never see a value of writing an assert other than check it directly.

  7. Nuestra asistencia técnica es un servicio a domicilio, con lo que el cliente solo ha de preocuparse de recibir al técnicos el día programado. No lo olvide le ofrecemos una asistencia inmediata sin aumentos en la tarifa por ser urgente. Disponemos del servicio tecnico más eficaz y económico de reparacion de frigorificos y frigorificos industriales en la Comunidad de Madrid. Nuestros tecnicos ofrecen un servicio de repuestos, cambios de piezas y reparaciones de frigorificos de cualquier marca. Servicio tecnico a domicilio sin cobrar desplazamiento, ofreciendo un presupuesto totalmente ajustable y gratuito. Resolvemos cualquier tipo de avería y lo hacemos en menos de 24 horas con nuestro servicio tecnico.

Recent Posts

April 17, 2024
January 13, 2024
December 29, 2023
February 12, 2017
December 18, 2016