* sum types (would be really nice)
* some equivalent of ? in Rust (can live without but would be nice)
After playing with a ton of languages have settled on Go for most projects.
* sum types (would be really nice)
* some equivalent of ? in Rust (can live without but would be nice)
After playing with a ton of languages have settled on Go for most projects.
That seems really difficult to retrofit, and to not really fit the language either.
I've been thinking something like sealed interfaces would fit better: Go already has fallible downcasts (and RTTI which takes the role of discriminant), and type switches can play the role of your `case` statement:
func do[T any](i option[T]) {
switch v := i.(type) {
case T:
fmt.Printf("Got a %v", v)
case nothing: // or `nil` could be a (pseudo)-type
fmt.Printf("was empty!")
// no default necessary because the check is complete
}
}
The only pieces missing are a way to "seal in" a set of types, and add completeness check to type switches for those.You can have "sealed interfaces" today; you put an unexportable method name in the interface. Then no legal external implementation can exist. (Not even if someone "guesses" the method name; the compiler will not consider them to have the same name.)
What you still don't get is completeness checking even so. In theory a linter could do it even without compiler support, I don't know if one exists.
You can use this in Go today to get, oh, say, 1/3rd of the feature of 'sum types' today. But you don't get much of the "sum types" bang for that 1/3rd of the buck. Basically, you can use them, and you don't need to do the fully manual type of thing you need to do in C with unions and tags, you can lean on the interfaces carrying type information around to handle that, but you get no additional compiler or syntax support, nor any sort of pattern matching on them.
You can get some of the benefits today with no changes to Go. You can create a package-level sealed interface: http://www.jerf.org/iri/post/2917
"I don't understand why you insist on quoting "sum types" when literally just mentioning them."
Because they aren't literally sum types, in that they have every feature that everyone associates with sum types, and people are very sensitive about that. So I don't need anyone replying with "but those aren't really sum types", either for my previous HN post or the blog post I just linked. Yes, I know they don't check every check box. But they do check a few of them, and, arguably the most essential one which is that you can indeed implement
type ASumType interface {
unexportedMethod()
}
func SomeFunction(thing ASumType) { ... }
and ASumType can be any of several types, closed and limited at compile time to only be things defined in this particular package in a way that can not be extended by any other external package.This isn't so different from the problem Generics introduce (why isn't this API generic, oh, it pre-dates that feature). And it might similarly be worth it for Sum types, but it's certainly an extra consideration.
On the technical side, Sum types kinda suck unless you have niches. If your Optional sum type is always actually bigger than the Some type it is wrapping then why even bother? So then you're also adding promises about niches in your implementation.
It's a brand new kind which wouldn't necessarily work the way existing types do, and it's a whole lot of extra syntax.
Plus previous retrofits have not exactly panned out great, C++ and Java's native "type safe enumerations" are pretty crummy as they're closer to enumerated sets of constants than sum types.
Sealed classes/interfaces/traits/… fit well in the machinery and aesthetics of RTTI-heavy products-types-based languages and slot nicely into the "niche" of sum types.