That said, there is a vast design space between Javascript/Python and dependent types. Plain old ADT suffice in 95% of cases, yet the only mainstream language with ADTs is Rust. This is a shame.
Swift, Kotlin, Scala and Typescript all support forms of ADTs, just with different names.
- Swift calls them "enums" (like Rust)
- Kotlin and Scala has "sealed classes"
- Typescript has "discriminated unions"
Kotlin (and even Java 15+ nowadays) can emulate ADTs with sealed classes, but the ergonomics are incredibly bad and the ecosystem is not build around the concept.
Scala has ADTs, but I would put Scala in the same category as Haskell or OCaml. A niche language.
Typescript does not have ADT, they have union types. You can build a discriminated type out of a union type, but you have to do this manually. This misses on the ergonomics of using ADTs plus people do it in different ways.
ADTs in one way or another are becoming more mainstream, but they are very far from being accepted by default.
I quite like Typescript's approach. You do get exhaustive switch-case matching, so that's like 80% of what I want out of sum types. Typescript also lets you enforce that a type is one of the variants of a sum type, something which e.g. Haskell doesn't let you do. I assume this is a function of Typescript doing union types, but it's pretty convenient.
Definitely see ADTs becoming mainstream, because they are genuinely useful. Biggest gap to adoption imo is that no SQL database supports sum types or anything equivalent.
I can see that if I made the branch return a string, Typescript will correctly show the "Not all code paths return a value" error
record Point(double X, double Y);
is immutable, whereas the mutable equivalent is the much more verbose: record Point {
public required double X { get; set; }
public required double Y { get; set; }
}
Java goes even further by not even having syntax for mutable records - if you want something like that, you have to write it out as a regular class.But yes, it's good that languages are moving in this direction.
My team lead used to shoot down ideas if they were "too complex". Correct and wise, right?
Except that this numskull created reams of functions that took untyped python dictionaries, did something, and passed them to other functions.
Early on, he bolted a giant mess of validation onto the perimeter. This did add value... but most data invalidity arose inside the fucking app!
He had a great eye for the complexity of abstractions, but was completely blind to the complexity of doing simple things in convoluted ways.
It was miserable. Any random PR could pass all the integration tests we had and still blow up prod, because there were orders of magnitude more code paths than lines of code.