I recently (like, in the last couple of weeks) picked up a Go project from another team at work (coming from a largely PHP background commercially, but with a lot of dabbling in other languages like Swift in my spare time).

I’ve enjoyed my initial foray into it so far. I like how small the surface area of the language is. I felt comfortable reading the code and starting to make changes within a couple of hours. While I love fancy complex features as much as the next nerd, there is something to be said about not having enough rope to hang yourself, especially when working in a team.

I like having standard first-party tooling including a formatter. Goroutines seem super neat, as do channels. People say Go’s generic system is a little tacked-on, but PHP doesn’t have one at all and Swift’s can get quite complex, so I’m happy with it.

I did find myself disappointed by a few things though, mostly around the type system. People consider PHP to be an untyped mess, but these days it actually has a reasonably expressive system that offers a lot of guardrails when paired with a static analyser. Swift has probably my favourite type system of any language I’ve used. Some complaints, in no particular order:

I think zero-values are a mistake when you have a compiler that could make usage of uninitialised variables a compile-time error (Swift does this). I want the compiler to yell at me when I forget stuff, rather than just rolling with it until it inevitably causes some logic error somewhere.

I sorely miss sum types and the ability that affords to strictly model a domain. It’s all well and good having linters that complain when I don’t handle an error, but I’d much prefer a compile-time guarantee that the return value of this function is a Value OR an Error, with the compiler forcing me to handle both cases. Granted PHP doesn’t have this, but it’s also a nearly 30 year old language that grew out of a simple templating engine. Swift is of course militant about this; you can’t even write a switch statement without handling all cases.

Further to the previous point, there doesn’t seem to be a particularly ergonomic way to model optional values, besides using pointers and checking for nil. While PHP doesn’t actually enforce that you check before doing something with a value, nullability is part of the type (if one is supplied). I can define my function to take an `?int` in order to accept null as a value, but if I define it with just `int`, I am guaranteed to not receive null (albeit at runtime, because there’s no compilation step). Swift of course has the Optional enum with the associated syntactic sugar to make this very nice to deal with. It would seem that in Go, I would need to add `if ptr != nil` everywhere to have the same sort of guarantee, though I am very new to the language so there is perhaps some better way of handling this.

No ternary operator…I accept the argument that nested ternaries are a readability nightmare but come on guys, given the infrequency in which they pop up in real-world codebases it doesn’t justify the visual noise that is “var-if-assignment-else-assignment”.

Anyway, despite my griping it’s a neat language, just a shame the designers seem to have deliberately ignored so many advances in type systems over the last few decades.