# Dawid Ciężarkiewicz aka dpc

## TL;DR

The biggest strength of #Go, IMO, was the FAD created by the fact that it is “backed by Google”. That gave Go immediate traction and bootstrapped a decently sized ecosystem. Everybody knows about it, and have a somewhat positive attitude thinking “it’s simple, fast, and easy to learn”.

I enjoy (crude but still) static typing, compiling to native code, and most of all: native-green thread, making Go quite productive for server-side code. I just had to get used to many workarounds for lack of generics, remember about avoid all the Go landmines and ignore poor expressiveness.

My favorite thing about Go, is that it produces static, native binaries. Unlike software written in Python, getting software written in Go to actually run is always painless.

However, overall, Go is a poorly designed language full of painful archaisms. It ignores multiple great ideas from programming languages research and other PL experiences.

“Go’s simplicity is syntactic. The complexity is in semantics and runtime behavior.”

Every time I write code in Go, I get the job done, but I feel deeply disappointed.

I think the best description of Go’s problems was Why Go Is Not Good.

Also interesting:

## More on my experiences with Go

While I was following Go #programming #language since it’s announcement and even did some learning and toying with it, only recently I had the opportunity to try Go in real projects. For the last couple of months I’ve been working a lot with Go, and below is why I think Go is simply a bad programming language.

### Nil

nil/null should not exist in any modern programming language.

nil keeps being the biggest source of frustration when working with code written in Go. nil handling is inconsistent too: sometimes nil being OK to use, and sometimes not so much.

### No sane error handling

o1, err := DoSomething1() if err != nil { return } defer Cleanup(o1)

o2, err := o2.DoSomething2() if err != nil { return } defer Cleanup2(o2)

oN, err := oN-1.DoSomethingN() if err != nil { return } defer CleanupN(oN)

compare the above with corresponding Rust code:

let o1 = try!(DoSomething1()); let o2 = try!(o1.DoSomething2()); … let oN = try!(oN-1.DoSomethingN());

or

DoSomething1() .andthen(|o1| o1.DoSomething2()) .andthen(|o2| o2.DoSomething2()) … .and_then(|oN| oN.DoSomethingN())

You might ask, where are the cleanup calls. Well, because Rust has guaranteed, predictable destruction, you don’t need cleanup calls 99% of the time! Files and sockets will be closed, services shut-down, data stores saved, etc.

Mildly-satisfying workarounds are given

### Lack of generics

It seems to me the lack of generic support is the root cause of all other problems.

Go has nil because without some form of generics, having an Option is not possible.

Go has poor error handling, as something like Rust’s Result can not be used without generics.

Go has no proper collections, and you weird make invocations are required because it lacks generics.

### All the annoying things

I fiddle with the code to try something out, I get an error that something is not used anymore. I have to remove the import to make the compiler happy. Then add it again.

Why it can’t be just a warning? Why is it that important for all the imports to really be used?

Or compiler errors because { is on a newline. Madness.

Go compiler is happy when I don’t check errors returned by a function (maybe by mistake), and I have to use go vet and golint to ensure there are no real obvious issues with my code, but it just won’t let me be when it comes to irrelevant details.

### Small things

How do you decrement an atomic in Go?