Gripes With Go
You’ve read a lot of posts about the shortcomings of the Go programming language, so what’s one more.
- Lack of sum types
- Type assertions
- Date and Time
- Statements over Expressions
- Erroring out on unused variables
- Error handling
Lack of Sum types
A “Sum” type is a data type that can hold one of many states at a
given time, similar to how a boolean can hold a true or a false, not too
different from an enum type in C. Go lacks
enum types unfortunately, and you are forced to resort to
crafting your own substitute.
A type to represent gender for example:
type Gender int
const (
Male Gender = iota // assigns Male to 0
Female // assigns Female to 1
Other // assigns Other to 2
)
fmt.Println("My gender is ", Male)
// My gender is 0
// Oops! We have to implement String() for Gender ...
func (g Gender) String() string {
switch (g) {
case 0: return "Male"
case 1: return "Female"
default: return "Other"
}
}
// You can accidentally do stupid stuff like:
gender := Male + 1The Haskell equivalent of the same:
data Gender = Male
| Female
| Other
deriving (Show)
print $ "My gender is " ++ (show Male)Type Assertions
A downcast with an optional error check? What could go wrong?
Type assertions in Go allow you to do:
var x interface{} = 7
y, goodToGo := x.(int)
if goodToGo {
fmt.Println(y)
}The error check however is optional:
var x interface{} = 7
var y := x.(float64)
fmt.Println(y)
// results in a runtime error:
// panic: interface conversion: interface {} is int, not float64Date and Time
Anyone that has written Go previously, will probably already know what I am getting at here. For the uninitiated, parsing and formatting dates in Go requires a “layout”. This “layout” is based on magical reference date:
Mon Jan 2 15:04:05 MST 2006
Which is the date produced when you write the first seven natural numbers like so:
01/02 03:04:05 '06 -0700
Parsing a string in YYYY-MM-DD format would look
something like:
const layout = "2006-01-02"
time.Parse(layout, "2020-08-01")This so-called “intuitive” method of formatting dates doesn’t allow
you to print 0000 hrs as 2400 hrs, it doesn’t
allow you to omit the leading zero in 24 hour formats. It is rife with
inconveniences, if only there were a tried and
tested date formatting convention …
Statements over Expressions
Statements have side effects, expressions return values. More often than not, expressions are easier to understand at a glance: evaluate the LHS and assign the same to the RHS.
Rust allows you to create local namespaces, and treats blocks
({}) as expressions:
let twenty_seven = {
let three = 1 + 2;
let nine = three * three;
nine * three
};The Go equivalent of the same:
twenty_seven := nil
three := 1 + 2
nine := three * three
twenty_seven = nine * threeErroring out on unused variables
Want to quickly prototype something? Go says no! In all seriousness, a warning would suffice, I don’t want to have to go back and comment each unused import out, only to come back and uncomment them a few seconds later.
Error handling
if err != nil { ... }Need I say more? I will, for good measure:
- Error handling is optional
- Errors are propagated via a clunky
if+returnstatement
I prefer Haskell’s “Monadic” error handling, which is employed by Rust as well:
// 1. error handling is compulsory
// 2. errors are propagated with the `?` operator
fn foo() -> Result<String, io::Error> {
let mut f = File::open("foo.txt")?; // return if error
let mut s = String::new();
f.read_to_string(&mut s)?; // return if error
Ok(s) // all good, return a string inside a `Result` context
}
fn main() {
// `contents` is an enum known as Result:
let contents = foo();
match contents {
Ok(c) => println!(c),
Err(e) => eprintln!(e)
}
}Conclusion
I did not want to conclude without talking about stylistic choices,
lack of metaprogramming, bizzare export rules, but, I am too busy
converting my interface{} types into actual generic code
for Go v2.
I'm Akshay, programmer, pixel-artist & programming-language enthusiast.
I am currently building tangled.sh — a new social-enabled code-collaboration platform.
Reach out at oppili@libera.chat.