Hacker News new | ask | show | jobs
by eximius 164 days ago
Lack of enums are the main point for me.

The error story is not ideal but less bad than that most of the time, as you can downcast to access extra error data. Still, harder than it needs to be.

Overall, I've grown to like using the language even despite its warts.

3 comments

I find the error story a horror one, mainly because it's quite easy to omit an error check when the err variable is reused which leads to weird crashes and garbage data, same happens when the error is explicitly ignored.
What is with people and their need for enums? Functionally using go const with iota gives you the same damn thing and people use enums that way 99% of the time. I find Rusts reliance on enums annoying as hell. At this point I consider Rust a bandwagon language. The syntax is abysmal and we have had memory safe languages far before Rust. That I wont get into because as a Vulnerability Researcher I find the Rust push super misguided and it sets me off.
To be more clear, I want sum types with exhaustive matching - which Go does not support.

I get by without it Go enums are an inferior representation of the same logical concepts. Sure, I can have (kind, value) and cast things for a hacky sum type for some kind enum. But Go lacks closed enums/exhaustive matching.

You can at least validate the match arms with things like type switches and marker interfaces, but they're still not exhaustive and they're terribly verbose.

And, again, I can get by without them! But I miss them because Rust-style enum representation comes up _so often_, even if you don't like the rest of Rust.

> What is with people and their need for enums?

I mean, you have atomic and compound data types. Atomic ones represent single values, like "a string" or "an integer", and compound ones represent multiple atomic types combined in some way, like a struct or an enum. Enums are useful for the same reason structs are useful, they do the same core thing, just model it in a different way. It's the difference between "and" and "or", which are both useful tools.

It doesn’t have the enum keyword, but there is an idiomatic way to make enums in the language. They just end up being typed constants.
When people say they want sum types, they generally mean they want both sum types and exhaustive pattern matching. Either of these features on isolation are nice to have, but both together are incredibly powerful to the point where having just one is almost not worth it.
Is this what people mean by Enums? I must not have used languages where Enums have those powers. I have often been very confused by statements people make about Enums so that would make some sense.

How do sum types address the problem that the underlying type (int or string or whatever) is totally capable of describing values other than those your code was compiled with? I'm mostly thinking of version skew with something like a dynamic library or plugin, although casting would probably also have the same effect.

Yes, this is generally what people mean when they talk about enums in modern languages.

The in memory and ABI representation of enums/sum types is language dependent.

In Rust, an enum is equivalent to a C union where there's a discriminant value which is an integer of sufficient size to cover all variants (u8 is sufficient for 255 different variants), followed by as many bytes as the largest variant (a tagged union). Mucking around with the underlying bytes to change the discriminant and cause type confusion is UB. All the same strategies you'd take in C for versioning and ensure binary backwards compatibility would apply. When exposing these you would likely want a higher level abstraction/self-describing wire format, if you don't have control over both sides of the ABI boundary.

Other higher level languages do this for you already. I believe Swift is one of those, where it has a COM-like ABI layer that the compiler makes injects transparently to correctly interact with a dynamic library, handling versioning for you.

I am of the opinion that we need a new attempt at a multi-platform, language-agnostic, self-describing ABI, "COM for the modern world". I think some people have talked about using WASM for this.

Keep in mind that the concept (pattern matching, sum types) is not tied to how they are represented (Scala has both, but they are represented different to what I described earlier; IIRC it uses inheritance to represent the variants).

The problem is that this makes enums non-enumerable. They need to be represented as a range or union type to do that. I am pretty sure I know why/how Go ended up like this because it’s inherited behavior from proto, wrote a large comment later down the thread explaining why.