6 ms·
Enums in Go
- asabla 2y agoI wonder if go ever will get some sort of enum type? Or if int/string based types will be the goto way?
- stpedgwdgfhgdd 2y agoIf there is one thing that I find missing is proper enum support in the Go language. There are quite a few subtle caveats you can run into without proper support in the language like the article mentions. (E.g. Using iota, passing in an undefined enum)
- ramon156 2y agoThis might be a very simple and ignorant thought, but I'd be fine if they completely worked the same as Rust's enum's. [if let] is such a powerful feature, along with match arms.
- CraigJPerry 2y agoThat’s not an enumeration like the parent and grandparent are discussing (think of a collection of consts), that’s a tagged union you’re talking about. Confusing because rust used the name enum in their syntax for this (Haskell calls it data - well, Haskell uses data in their syntax for both product and sum types).
- tialaramex 2y agoWhat we're discussing here is always an actual sum type though, it's just a question of whether you're interested in half-assing it, and Rust is not. There are people who want C's "surprise it's actually just an integer" which lets them use C's enums as bit flags, and that I agree isn't just a sum type, but the fact that Rust will let you sum things which aren't units isn't that this is "really" a tagged union, that's a possible implementation detail but not the core idea. This is not C++ std::variant which really is just a tagged union. Option<OwnedFd> isn't a tagged union, it's "just" much nicer sugar for C's signed integer type. Option<&str> isn't a tagged union it's sugar for a fat pointer which can be null. And so on.
- klabb3 2y agoRust got those basic type system and pattern matching things so incredibly right it’s not even funny. I even like Go more for other reasons but I still cringe every time I have to use the error prone hacks mentioned in this post. Not to mention the largest source of noise: `if err != nil`, which Rust solved so elegantly using partly their enum types.
- tialaramex 2y agoSure, but "the same as Rust's enums" including a mention of pattern matching, is a really big expenditure on the novelty budget from the point of view of Go. What Rust does there is perfectly normal... for an ML. But before Rust you didn't really see an ML down there counting CPU cycles, so this wasn't even on the radar when Go was invented. I think to do that you'd probably give up a lot of the simplicity Go is aiming for. I personally think that simplicity is somewhat illusion (Amos' "I want off Mr. Golang's Wild Ride" https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-... says it better than I could) but we should be clear that it's not that Go aimed to do what I want and missed but that it was never interested in that at all. An F1 car doesn't want to be useful for taking the kids to school, so it's silly if we're scoring it poorly for lack of child seat fixtures.
- masklinn 2y ago> Sure, but "the same as Rust's enums" including a mention of pattern matching, is a really big expenditure on the novelty budget from the point of view of Go. Following the addition of type sets for generics I think go actually has all the pieces for union types already and it’s a matter of putting them all together at the compiler level: - allow type sets as types (currently they’re only valid for constraints), probably excluding those using “underlying types” - implement match completeness for type switches over type sets And there you go, you’ve got unions from which you can easily implement sums via type declarations: type Foo int type Bar struct {} type FooOrBar interface { Foo | Bar } func Thing(v FooOrBar) { switch vv := v.(type) { case Foo: // you have a foo case Bar: // you have a bar // a default case is required if the cases are not exhaustive, forbidden if they are } } Is this perfect? Not even remotely, this suffers from the usual Go issues of zero values, unenforceable constructors, and nil interfaces. But these are issues of the language, they should be fixed in the language in a hypothetical Go 2, I don’t think there is a good reason to try and work around them here. Also completeness requirements could probably be extended to all “trivial” switches (types or values, not generalised expressions) via a go.mod stricture, similar to the new loop semantics.
- techn00 2y agoI use https://github.com/orsinium-labs/enum https://github.com/orsinium-labs/enum
- daghamm 2y agoI'm implementing a somewhat complex software in both Go and Rust. The Rust version is bith shorter and more readable - and probably more efficient - thanks to Rust enums and Rust error handling. I don't understand why golang doesn't copy Rust here. The error handling in particular could be a very simple change. I am not a huge fan of go:generate and similar projects. They add a level of unknown that goes against the core Golang design values.
- JetSetIlly 2y ago> I am not a huge fan of go:generate and similar projects. They add a level of unknown that goes against the core Golang design values. I'm not sure I would agree with that. go:generate is a core part of Go since v1.4 and the enum generators are the kind of thing that was intended. https://go.dev/blog/generate https://go.dev/blog/generate That said, enums would be a welcome improvement to the language. But even then, I think go:generate has a place.
- frou_dh 2y ago"add(ing) a level of unknown" is a very fair description from a code comprehensibility standpoint, since the go:generate directive is about running arbitrary executables.
- JetSetIlly 2y agoTrue.
- foldr 2y agoThe is ameliorated somewhat by the fact that go generate is not part of the build process. Its output would typically be checked into the repo. So as long as the generated code is reasonably readable, it should not have too much of an impact on code comprehensibility.
- foldr 2y agogo:generate is mostly just a marketing failure. If they'd called it 'procedural macros', everyone here would think it was the bee's knees.
- altug 2y agoI really miss value enums from Rust while working with Go, but overall I find Go more intuitive. Didn't know there was a way of auto-generating with stringer, so thanks for the information!
- akira2501 2y agoUse a slice of strings? type Color int const ( Red Color = iota Green Blue ) var Colors = []string{ "Red", "Green", "Blue" } Now (Colors[Red] == "Red") and (slices.Index(Colors, "Green") == Green).
- slekker 2y agoYou could do the same with a map[Color]struct{} and the lookup would be "if _, ok := Colors[Green]; ok {"
- JyB 2y agoCompile-time safety is not achieved which is the point.
- akira2501 2y agoIt does not exist in the implementation. We can discuss workarounds which make it more convenient and directly address one of the points in the article, or we can discuss something which is definitely not going to change in the lifetime of the language. There is no language that completely isolates you from runtime hazards.
- rty32 2y agoIf you add an enum entry but forget to update the slice, BOOM And that's exactly the kind of thing people are discussing here.
- dgb23 2y agoI recently wrote a little tokenizer in Go. The data structure for a token that makes most intuitive sense to me is a tagged union. So I defined an „const iota“-style enum. Stuck it into a struct that has the appropriate fields to cover all the cases and it was fine. Having some syntax sugar for tagged unions would be nice. Having exhaustiveness checks if you switch over them, could be useful in some cases. But that’s not where my mental energy went at all. Reading the bytes (runes) efficiently and correctly into the data structure however is the part that needs focus. Once the data is in shape, I‘m not „worried“ at all anymore. Sure a bit of extra support is nice, but also kind of superficial. Also going further, dispatching on them is again never the tricky part. It’s handling the cases correctly and efficiently that has my focus. In Clojure, a common thing is to write multimethods that dispatch on (namespaced) keywords. Similar in spirit and structure, but each method might now reside in a different namespace or not even be written by you. But I have never worried about exhaustive matching or similar. What’s in the method bodies is the important part.
- barnabee 2y agoI’ve found the enumer [0] library does the job of generating usable helpers without too much pain or any obvious downsides. The ability to generate JSON [un]marshallers is particularly handy. Still, the lack of enums and enum/sum types remains by far my biggest gripe about Go. [0] https://github.com/dmarkham/enumer https://github.com/dmarkham/enumer
- deleted 2y ago[deleted]
- PhilippGille 2y agoThe posted article mentions https://github.com/abice/go-enum https://github.com/abice/go-enum, which does the same thing, no? It even generates the consts based on a list of values in a comment.
- frou_dh 2y agoFour candidates mentioned so far in the HN comments so far. Here's 45? https://github.com/search?q=enum+generator+language%3AGo&type=repositories&l=Go https://github.com/search?q=enum+generator+language%3AGo&typ... https://github.com/search?q=enum+generation+language%3AGo&type=repositories&l=Go https://github.com/search?q=enum+generation+language%3AGo&ty...
- barnabee 2y agoIt does indeed seem to be similar, and viable option, with the difference being that the enum values are specified in a comment. You seem enthusiastic about that design decision/feature, howeever I am not sure about it, it scares me a little. It's absolutely just a hunch and personal preference but I worry that keeping the enum value inputs in comments might not be great for new contributors and in terms of maintaining the codebase over time, so I think I prefer the approach taken by enumer.
- nikolayasdf123 2y ago+1. Here is another alternative. I find this generator more lightweight and better syntax (generated benchmarks and tests are also nice): https://github.com/nikolaydubina/go-enum-encoding https://github.com/nikolaydubina/go-enum-encoding
- weavie 2y agoThe problem isn't so much the lack of enums, it's more that there is no way to do exhaustive pattern matching at compile time. I have seen production systems go down because someone added a variant to an 'enum' but failed to handle that new variant everywhere.
- dgb23 2y agoIsn’t that the first thing you would do if you add a new variant?
- K0nserv 2y agoOnly if you remember, which you, or someone else, is bound to not eventually
- rty32 2y agoadded that every switch/if should handle this exhaustively. For any project with more than a few dozens of files, it is basically impossible to remember all the downstream code that uses the enum -- you have to track it down, or better, let compiler automate check all usages
- dgb23 2y agoBut you're not adding variants without a reason? You want them to have some effect. It's hard for me to think of an example where it would make even sense to "having to remember to handle the variant" rather than "handling the desired effect of the variant".
- K0nserv 2y agoPeople are stressed, get distracted, are tired, don't have complete knowledge etc. This is kind of like arguing that null pointers aren't a problem, you "just" have to check all usage of the pointer if you make it null. In practice we know solutions like this don't work
- weavie 2y ago
- witx 2y agoI can't take this language seriously. No enums, letter cases defining if something is "public" or "private", generics as an after-thought. To name just a few
- rty32 2y agoTo be fair, generics was an after-thought because Go was originally a "small" language used internally at Google, and they needed to ship the language in a working state. Generics was too complicated to handle for the first versions (according to Russ Cox). If you look at Java, generics didn't exist until a few major versions in. Although I agree it probably should be done quite a bit earlier.
- bborud 2y agoEnums in Go are not good. Code generation just makes it worse. Both because code generation has a bad smell and because nobody can agree on how to do enums in Go so we just end up with lots of diverging solutions. Go 2 needs to have more usable enums. And while I'm not a big fan of "adding more stuff" to languages, it wouldn't hurt Go to learn a couple of things from Rust.
- jaitsu 2y agoI can't see why they wouldn't be in a future 1.x release
- bborud 2y agoI guess it depends on how much of a departure from the current language it would be and if that introduces complicating factors. It would make sense to seriously consider adding matching (like in Rust) but then you might want to consider more than just enums? I'm not a language design expert, but I suspect there be worms in that there can.
- candiduslynx 2y agoOh, the missing wonders of `[...]`: https://go.dev/play/p/GlVp_z3IOEe https://go.dev/play/p/GlVp_z3IOEe
- pjmlp 2y agoBesides the iota dance hack of "enums" in Go, apparently Go 1.23 is going to bring another one, magic fields. https://pkg.go.dev/structs@master https://pkg.go.dev/structs@master What more sane languages would use attributes for, Go 1.23 will do it like this, type myStruct struct { _ struct.HostLayout } Lovely design.