Live data from Hacker News

Compile-time safety for enumerations in Go

vladimir.varank.in

1–10 of 116 posts

Re: Compile-time safety for enumerations in Go

#2
I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure.

What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

Re: Compile-time safety for enumerations in Go

#4
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I go back and forth on this. On one hand, I _love_ Rust/ML style sum types. They're such a fantastic feature. On the other hand, I wonder how much use they'd get in a language like Go. If it's introduced, would it radically change the way some problems get solved? Is it that different than an traditional enum (which Go also skirts around)?

Pattern matching and exhaustive checks are massive benefits of them though. I guess I'm just oddly conflicted here.

I do think that long term, sum types are going to become more prevalent. I'm excited to see it, I'm just interested in how that adoption occurs in existing languages without them.

Re: Compile-time safety for enumerations in Go

#5
post #4
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I go back and forth on this. On one hand, I _love_ Rust/ML style sum types. They're such a fantastic feature. On the other hand, I wonder how much use they'd get in a language like Go. If it's introduced, would it radically change the way some problems get solved? Is it that different than an traditional enum (which Go also skirts around)? Pattern matching and exhaustive checks are massive benefits of them though. I…

If I had sum types in Go, I'd use them for state machines and enumerations mostly. I can't see a huge downside; I'm sure some software has little use for it, but it's useful enough for data modelling that protobuf has the sort-of similar oneof primitive. Hmm. On that note, maybe it would be better than nothing to do some kind of code generation here...

Re: Compile-time safety for enumerations in Go

#6
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

If you are satisfied with a dummy method on an interface, you can continue by adding more types to that interface. Color is not a great example, so:

    type Vehicle interface {
         isVehicle()
    }

    type Car struct {}
    func (c Car) isVehicle() {}

    type Van struct {}
    func (v Van) isVehicle() {}

    func VehicleType(vehicle Vehicle) {
        switch v := vehicle.(type) {
        case Car:
            fmt.Println("car")
        case Van:
            fmt.Println("van")
        default:
            fmt.Println("unknown vehicle")
    }
This covers most, but not all, of the bases, in that you don't get exhaustiveness checking at compile time, unless you adjoin a linter to your compile process: https://github.com/BurntSushi/go-sumtype

Re: Compile-time safety for enumerations in Go

#7
post #4
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I go back and forth on this. On one hand, I _love_ Rust/ML style sum types. They're such a fantastic feature. On the other hand, I wonder how much use they'd get in a language like Go. If it's introduced, would it radically change the way some problems get solved? Is it that different than an traditional enum (which Go also skirts around)? Pattern matching and exhaustive checks are massive benefits of them though. I…

I think the answer to your "back and forth" is probably laid out in: https://jerf.org/iri/post/2960/

Sum types are useful, even very useful in the right place, but I do think there's a lot of people who use them a couple of times, probably in one of those "right places" and then mistakenly label them in their mind as "better". Just, universally, Platonically "better". They aren't in fact "better"; they're a tool. Sometimes they're the right tool for the job, but much, much more often, they're just a tool that works, and so do several other tools. People who get too excited about sum types need to be sure they square their understanding of how useful they are with the fact that the vast majority of programs are written without them.

Re: Compile-time safety for enumerations in Go

#8
I sometimes do this but idk if I would consider it 'elegant'

The other 'gotcha' is that in switch statements the compiler can't tell whether you enumerated on all your cases as there is no true enum type so it's not uncommon to have a catch all default case that either returns an error or panics and hope you can catch it during tests if you missed a case.

I just wish go had proper sum types.

Re: Compile-time safety for enumerations in Go

#10

> Go’s type system allows preventing both issues in a rather elegant way. Proper enums would be elegant, not this.

Some way to define a closed set of values anyway. It doesn't even need to be classic-style sum types, for instance as support for generics Go introduced support for union types (I don't think they have a name?) e.g.

    type Foo interface { A | B | C }
and the interface type is the union of those type-sets. Such interfaces can not currently be used outside of type constraints, but if that is relaxed, and type switches are updated to support and enforce exhaustive matching (and understand such sealed / nominative interfaces), you've got all the bits you need.

You'd need to newtype variants to add payloads of similar underlying type e.g. `int | int`, but that's not a huge imposition, and the variants being types themselves is often convenient so it's a 50:50 tradeoff compared to classic sum type (where constructors disambiguate all variants but are not themselves types).

Post reply on HN