What does HackerNews think of go-sumtype?

A simple utility for running exhaustiveness checks on Go "sum types."

Language: Go

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
The call out to sum types is something I feel. I've been using Rust daily for almost 10 years now, and sum types are absolutely still one of the things I love most about it. It's easily one of the things I miss the most in other languages that don't have them. I'm usually a proponent of "using languages as they're intended," but I missed exhaustiveness checking so much that I ported a version of it to Go[1] as a sort of lint.

[1]: https://github.com/BurntSushi/go-sumtype

Not that it's a great solution, but there's https://github.com/BurntSushi/go-sumtype for (2).
Or even better, proper sum types. They're a superset of enums anyway.

https://github.com/BurntSushi/go-sumtype is great, but a bit unwieldy. Language support would be much better.

Go switches with exhaustion checks like in [1] would go a long way. An early return construct would be important as well, though, you're right.

Go's for loops can only iterate over builtin types: slices, maps, channels, etc. You can't produce a custom type that is to be iterated over.

1. https://github.com/BurntSushi/go-sumtype

go-sumtype[0] has completeness checking for sealed interfaces.

[0] https://github.com/BurntSushi/go-sumtype

go-sumtype[0] has completeness checking for sealed interfaces.

[0] https://github.com/BurntSushi/go-sumtype

Good post! Crossposting my comment from that other crusty news aggregator:

Alternative 1a is to use Alternative 1 with https://github.com/BurntSushi/go-sumtype /plug

go-sumtype requires the interface to be sealed (which you're already doing) and one small annotation:

    //go-sumtype:decl TheInterfaceName
Then you just run `go-sumtype`

    $ go-sumtype $(go list ./... | grep -v vendor)
and it will do exhaustiveness checks in any type switch in which `TheInterfaceName` participates. This will prevent the "For example, during a refactor a handler might be removed but a type that implements the interface is not." failure mode mentioned in the article.
This looks promising! We use the go-grpc SDK in conjunction with gogoprotobuf, and it's been a rocky road.

While the article identifies some operational issues (e.g. the reliance on HTTP/2), there are several considerable deficiencies with gRPC today, at least when using it with Go:

1. The JSON mapping (jsonpb.go) is clumsy at best, and by this I mean that it produces JSON that often doesn't look anything like how you'd hand-design your structures. "oneof" structs, for example, generate an additional level of indirection that you typically wouldn't have. Proto3's decision to forego Proto2's optional values (in Proto3 everything is optional) cause Go's zero value semantics to leak into gRPC [1]. (We had to fork jsonpb.go to fix some of these issues, but as far as I can tell, upstream is still very awkward.)

2. The Go code generator usually produces highly unidiomatic Go. "oneof" is yet again an offender here. The gogoprotobuf [2] project tries to fix some of go-grpc's deficiencies, but it's still not sufficient. Ideally you should be able to use the Proto structs directly, but our biggest gRPC project we basically gave up here, and decided to limit Proto usage to the server layer, with a translation layer in between that translates all the Proto structs to/from native structs. That keeps things clean, but it's pretty exhausting work, which lots of type switches (which are hampered by Go's lack of switch exhaustiveness checking; we use BurntSushi's go-sumtype [3] a lot, but I don't think it can work for Proto structs, as it requires that a struct also implements an interface).

3. Proto3 has very limited support for expressing "free-form" data. By this I mean if you need to express a Protobuf field that contains a structured set of data such as {"foo": {"bar": 42}}. For this, you have the extension google.protobuf.Value [4], which supports some basic primitives, but not all (no timestamps, for example) and cannot be used to serialize actual gRPC messages; you can't serialize {"foo": MyProtoMessage{...}}. Free-form structured data is important for systems that accept foreign data where the schema isn't known; for example, a system that indexes analytics data.

From what I can tell, though, Twirp doesn't "disrupt" gRPC as much as I'd like, since it appears to rely on the existing JSON mapping.

[1] https://github.com/gogo/protobuf/issues/218

[2] https://github.com/gogo/protobuf

[3] https://github.com/BurntSushi/go-sumtype

[4] https://developers.google.com/protocol-buffers/docs/referenc...

Not the same as what the article is trying to accomplish, which is an exhaustive match (i.e., any unmatched value is guaranteed to fail at compile time).

For example, consider a parser that matches on tokens. If you add a new token, the match should fail, because you want to guarantee, at compile-time, that every possible case is handled.

This is one reason that the lack of sum types in Go is so painful, to the point that someone wrote a special library for it [1].

[1] https://github.com/BurntSushi/go-sumtype

Check this out. With this tool, the type switch gets you exhaustiveness checks for hokey package-private sum-types in Go :)

https://github.com/BurntSushi/go-sumtype