Java has recently added sealed classes/interfaces which offer the same features as sum types, and I would argue that Java is definitely mainstream.
Kotlin has a similar feature. It might be used less than Java, but it's the default language for Android.
Swift has `enum` for sum types and is the default language for iOS and MacOS.
Likewise for Rust, which is gaining traction recently.
Typescript also has union/sum types and is gaining lot of traction.
How does the ABI work? If a library adds a new constructor, but I am still linking against the old version, I imagine that it could be reading the wrong fields, since the constructor it's reading is now at a different index?
optional_int: int | None = None
Python's `A | B` is a union operation, but in Rust a sum type is always a disjoint union. In Python, if `A = B = None`, then `A | B` has one possible instance.
In Rust, this sum type has two possible instances. This might not sound like a big deal, but the semantics are quite different.
def foo(int | None = None) ...
... just means the variable's default value is None in a function definition. But it could be either in an actual function call.
Say you wanted to define some function taking `YYMMDD | MMDDYY`. If both YYMMDD and MMDDYY are just aliases to `str`, then you gain no information, you cannot discriminate on which one it is, since the union `str | str` just reduces to `str`.
Sum types are disjointed unions, you can't just say `str | str`, the terms are wrapped in unique nominal data constructors, like:
enum Date { MMDDYY(String), YYMMDD(String) }
Then when accepting a `Date` you can discriminate which format it's in. You could do the same in Python by defining two unique types and using `MMDDYY | YYMMDD`.