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.
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.
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.
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.
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.
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).