Live data from Hacker News

Enums in Go

dizzy.zone

21–30 of 57 posts

Re: Enums in Go

#21
post #4

Earlier quoted context omitted.

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

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. 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 aimi…

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

Re: Enums in Go

#22
post #18

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

Isn’t that the first thing you would do if you add a new variant?

Re: Enums in Go

#23
post #18

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

There are two linters that add those checks: https://github.com/nishanths/exhaustive for values and https://github.com/alecthomas/go-check-sumtype for types. Both are integrated into golangci-lint. I use both a lot and have only a positive experience.

Having support from the language would be nice, though.

Re: Enums in Go

#24
post #8

I'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…

go:generate is mostly just a marketing failure. If they'd called it 'procedural macros', everyone here would think it was the bee's knees.

Re: Enums in Go

#25
post #18

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

The overarching lesson of my career has been that people are fallible and processes that rely on humans to not make mistakes are bound to fail. Recently I've also been thinking about the importance of being able to reason "locally" about code, it might be the single most important property of a software system. "locally" typically means a function, class, or module, but I think it can also be applied to "horizontal" cases like this. For example, if you add an enum variant the compiler should guide you to reason about each place where the enum is no longer exhaustively matched.

Re: Enums in Go

#26
post #22
post #18

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

Isn’t that the first thing you would do if you add a new variant?

Only if you remember, which you, or someone else, is bound to not eventually

Re: Enums in Go

#27
Enums 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.

Re: Enums in Go

#28
post #18

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

In theory yes. But practically there are always locations where we can't match every case, so we either have to live with a warning or add a catch-all arm. And as soon as a catch-all arm exists, we are in "not checked any more" state, but with a compiler that is supposed to check for exhaustiveness. Which is way worse if the catch-all arm isn't a `panic("Unhandled match branch!")`.

Yes, you can counter that with GADTs and match against exhaustive subsets. But to ergonomically handle these cases, you need something like Pattern Synonyms or you drown in boilerplate.

Re: Enums in Go

#30
post #18

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

In theory yes. But practically there are always locations where we can't match every case, so we either have to live with a warning or add a catch-all arm. And as soon as a catch-all arm exists, we are in "not checked any more" state, but with a compiler that is supposed to check for exhaustiveness. Which is way worse if the catch-all arm isn't a `panic("Unhandled match branch!")`. Yes, you can counter that with GADT…

As long as the set is limited (as enums usually are), you can always go with the middle ground between omitted case and catch all:

    case FILE_NOT_FOUND:
        /* This is normal, nothing to do. */
        break;
This should still catch adding new value into the enum and not handling it.
Post reply on HN