Go is a bad programming language
I need to take a minute to rant about the go language and a few of the many, many things wrong with it.
1. Unused variables are compile-time errors.
So if you're debugging this:
x, y := foo() doSomething(x) doSomething(y)
and you decide to comment out doSomething(x) in order to check something, then hey, your code doesn't compile anymore! No, if you want to comment out a few lines while debugging, you also have to go up and make sure you also remove any declarations or assignments to variables that are only used in that block.
This is absolutely infuriating, and is one of the least developer-friendly features I've ever seen (excluding languages that were explicitly designed to be terrible).
So why did they do it? Why would the designers make such a ridiculous decision? The answer is simple:
2. Go doesn't consistently distinguish assignment from declaration.
Some languages distinguish between declaring a variable (e.g. int x) and setting it (e.g. x = 12); others don't bother and just let x = 12 be declaration if needed. There are pros and cons to both, but Go manages the difficult task of achieving the worst of both worlds.
You can declare var x int and assign x = 12, but you can also combine the two with x := 12 - the := implicitly declares the variable being assigned.
Fortunately, go IS smart enough to not let you double-declare something - var x int; x := 12 is a syntax error.
But go also lets you assign multiple values. What do you think this does?
var x int = 1 x, y := 2, 3 fmt.Println(y) fmt.Println(x)
Did you guess that this is a syntax error? Wrong! It prints 3, then 2 - the := operator is fine with the fact that x already exists, as long as at least one of the variables you're assigning to does in fact need to be declared.
What about this: var x int = 1 if true { x, y := 2, 3 fmt.Println(y) } fmt.Println(x)
If you "the same thing", you're wrong! This one IS a syntax error! Why? Because now that it's in a separate block, that := is now declaring x instead of assigning to it! And the new x is unused, so the compiler can't let you do that! Note, though, that if the first print printed both x and y, the compiler would not have caught that for us. The code would just compile fine and be wrong. That is the golang experience.
So why would a language allow a := that changes meaning but only when there are multiple variables? Simple! It's because:
3. Go has no error handling whatsoever
No exceptions, no maybe monads, no anything. Just a convention that functions that might fail should return a pair of (result, error).
This means code that in a sensible language might look like
print(foo(bar(3), baz("hello")))
in go instead looks like
a, err := bar(3) if err != nil { return err } b, err := baz("hello") if err != nil { return err } c, err := foo(a, b) if err != nil { return err } fmt.Println(c)
Yay boilerplate!
So obviously you need := to allow some things to already exist, otherwise you'd have to give all those errs different names, and who has time to come up with that many variable names?
4. Don't use go.
Anyway, these are just three of the many, many things I hate about go, which also include
Switch statements for values vs for types have almost-but-not-quite identical syntax (they should either be the same or be different, preferably different).
Type switch naming is stupid (sensible languages let you name the variable in each case instead of just renaming the variable once at the beginning).
Unused imports are a compile-time error (so incredibly stupid and frustrating).
Capitalization distinguishes public values from private ones (result: APIs cannot use capitalization in a meaningful way, because by definition every member of the API must be capitalized).
Extremely limited type system (terrible type inference, no generic methods on objects, no co- or contravariance among functions or objects - a function that returns a Square is not a function that returns a Shape and cannot be used as one).
Any public type can be instantiated with a "zero value", so every object method must do something sensible if the object is completely uninitialized (I saw some code in one of go's own built-in libraries that represented "any nonnegative integer or undefined" as a pair of (val int, isZero bool); a value of 0 is undefined unless isZero is true, in which case it means 0. I don't think that would pass code review in any other language at all).
Surprisingly awkward and unintuitive concurrency support for a language that claims to have been built with concurrency in mind.
Weird and inconsistent special magical cases (e.g. if g() returns three arguments and f() takes three arguments, then f(g()) is fine; if g returns two, you can't f(1, g()) or anything like that).
More things that I won't spend any more time writing up, I just needed to get this out of my system.










