Skip to content

Instantly share code, notes, and snippets.

@jimmyfrasche
Created August 18, 2017 01:31
Show Gist options
  • Save jimmyfrasche/ba2b709cdc390585ba8c43c989797325 to your computer and use it in GitHub Desktop.
Save jimmyfrasche/ba2b709cdc390585ba8c43c989797325 to your computer and use it in GitHub Desktop.
Sum types experience report

Interfaces only allow you to model based on method set and they are, by design, open. Any type that satisfies the interface, satisfies the interface. They share some similarities to sum types, but, as far as they do, they are, essentially, infinite sums. While this often desired, there are times when you need to limit the options to a closed set of types.

There's no direct way in Go to say "these types, even though they share no methods in common". You have to use an interface{}, which says nothing—even though you know exactly what you want to say. You have to handle an invalid case at runtime.

There's no direct way to specify only the types in this package. Using an interface with an unexported "tag" method gets you part of the way. There's still nil and embedding and, at every point, those need to be dealt with—or, all too often, ignored.

These cases can lead to trivial errors that could be caught by the compiler, but instead need to be handled by defensive coding and extensive testing. Defensive coding and extensive testing will always be needed, of course, but it would be better if that effort could be focused on more pertinent, higher level concerns instead of having to buttress against an inability of the type system to understand "I can only handle these specific types". The larger a project gets the more opportunity is for the invariants to be violated and it is too easy to violate them.

Without a way to define a closed set of types, we end up doing the type checker's job whenever the need arises. Linting can help somewhat. I do not believe it can be done with 100% accuracy except in simple cases and there are enough variations within the "closed set of types" space to require multiple different kinds of linters.

The unnecessary boxing can increase allocations and be an optimization fence, leading to inefficient data structures and algorithms. With a way to model a closed set of types, the compiler has more information to work with because it has fewer possibilities to consider.

The lack wastes programmer time and machine spacetime.

These issues do not come up at every turn—unless you're writing a compiler/interpreter where they are pretty much the only turn. But they do still come up, and, when they do, there are no great options in the language.

When dealing with an FFI, there are often times when you need to specify "only these types" and that can be outside of your control. Similar situations can arise with serialization: json.Token being an example.

One of the more vexing issues I run into, outside scenarios like the above, is needing to have multiple channels in a select when it would be cleaner to have one or two with sum types representing the set of allowable messages and their respective disjoint values. That would better model the structure of the communication and I could only return one or two channels from the code that started the server instead of a handful.

Being able to say "every type like this" is extremely useful in general, but sometimes you need to be able to say "only these types".

@jimmyfrasche
Copy link
Author

Gists don't do notifications. It's very annoying. I'll leave the discussion because there are some good links.

@awalterschulze
Copy link

Yes very annoying :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment