The One Thing I'd Change About Go

Boot.dev Blog ยป Golang ยป The One Thing I'd Change About Go
Lane Wagner
Lane Wagner

Last published August 12, 2023

Subscribe to curated backend podcasts, videos and articles. All free.

Go is built for grug brained programmers like me.

grug brain developer not so smart, but grug brain developer program many long year and learn some things although mostly still confused

apex predator of grug is complexity

complexity bad

say again:

complexity very bad

you say now:

complexity very, very bad

given choice between complexity or one on one against t-rex, grug take t-rex: at least grug see t-rex

grugbrain.dev

The Go team took many years to add generics to the language. It was a good addition, and many argued that it was an obvious decision that should have been made sooner.

I disagree.

The simple truth is that when you’re building applications, especially back-end web applications or CLI apps (which is where Go shines as a language imo) you just don’t need generics all that often. They’re quite nice to have, but far from necessary.

Are you building a clever library using general-purpose data structures? Sure, generics make your life much easier. But let’s be real, I’m over here parsing JSON and shoveling strings into databases. I don’t need 3 layers of abstraction to get ’er done.

Smol-brain code work gud for application layer.

What else is Go good for? ๐Ÿ”—

Before I start shitting on my favorite language, let me point out some of the other reasons I love Go.

  1. Grug-brained syntax (already mentioned)
  2. Statically compiled binaries
  3. A toolchain with built-in formatting, testing and dependency management
  4. Dependency management built on Git repos (no npmjs.com or crates.io)
  5. A standard library that cares about the web
  6. Goroutines and channels (concurrency that doesn’t suck)
  7. Fast despite a garbage collector

What would I change about Go? ๐Ÿ”—

Not a hard question. It’s sum types! (Or enums, tagged unions, or whatever you want to call them).

Go currently has a shitty excuse for enums:

type Color int

const (
		Red Color = iota
		Green
		Blue
)

They’re pretty bad. Go’s “enums” are verbose, error-prone, and don’t ackshually enforce much of anything from a typing perspective. Let me show you what I mean.

It’s just an alias ๐Ÿ”—

type Color int

This is a type alias. At the end of the day, the new Color type is just an int. It’s not really a new type, it’s just a new name for an existing type. In this world, every integer on God’s green Earth is a valid color.

That’s crap.

If I wanted that I would just use an int.

I want to restrict the set of valid colors to a specific subset of colors, e.g. Red, Green, and Blue.

iota ๐Ÿ”—

The iota keyword in Go is a special feature that allows you to define a sequence of constants that increment by 1. Sound useful? It’s not. It’s just cryptic syntactic sugar.

A smol-brained developer like me might make a few constants like this:

const (
		Red Color = 0
		Green Color = 1
		Blue Color = 2
)

But a big-brained developer might save a few characters by using iota:

const (
		Red Color = iota
		Green
		Blue
)

Here’s the kicker: the iota method uses the same amount of lines of code, but now I need to count on my fingers and toes to figure out the actual value of any of these constants.

const (
		Red Color = iota
		Green
		Blue
		Grey
		Black
		White
		Yellow
		Orange
		Purple
		Pink
		Brown
		Chartreuse
		Mauve
)

What’s the value of Mauve?

Additionally, there isn’t even support to quickly marshal these integers into strings (e.g. for debugging) without writing mountains of boilerplate code.

Frankly I just pretend iota doesn’t exist and instead define string constants like a peasant:

type Color string

const (
		Red Color = "Red"
		Green Color = "Green"
		Blue Color = "Blue"
)

Despite the verbosity, nothing is safe ๐Ÿ”—

Believe it or not, I can still do this in Go:

type Color string

var carColor Color = "clearly not a color"

The compiler don’t care. The Color type we made is just an alias for string. Any string is a valid Color. Sure, I defined some constants, Red, Green, and Blue, but they’re just constants that I hope and pray and beg my team to use.

I have no way to enforce this stuff at compile time. So now my only choice is to write runtime checks everywhere I use the colors:

switch color {
case Red:
		// do happy thing
case Green:
		// do other happy thing
case Blue:
		// do other other happy thing
default:
		return errors.New("wHY dIDn'T yOU uSe a vALID cOLoR???")
}

Eew, runtime.

If there’s one thing I hate more than indenting with spaces it’s doing literally anything at runtime.

How to make Go better ๐Ÿ”—

I can’t believe I’m using TypeScript as an example of a language that does something well, but here we are. In TypeScript, you can define a sum type like this:

type Color = "Red" | "Green" | "Blue";

Shut up and take my money

Look at that. It’s simple. It’s elegant. It’s safe.

Sum types provide more type safety, more expressiveness, are easier to understand, all while being less verbose. It’s a win-win-win-win. If it weren’t for Go’s backward compatibility promise (which I love), I’d even rip out the stupid iota keyword in addition to adding sum types.

๐Ÿšจ๐Ÿšจ BREAKING: TypeScript snatches defeat from the jaws of victory ๐Ÿšจ๐Ÿšจ ๐Ÿ”—

TypeScript did it. They have beautiful beautiful sum types. They’re called unions in TypeScript, but they’re the same thing.

Then some galaxy-brained 10x developer decided it also needed an enum keyword so we’d have 2 ways to accomplish the same thing:

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

Write Go.

Be grug.

Pray for sum types.

Find a problem with this article?

Report an issue on GitHub