There are so many ways to break code, but hidden type overlap makes is surprisingly easy to break code and is quite hard to detect.
Iâm with @curoli. I would want to have a way to future-proof all my union types, to validate that none of the element types overlap.
Forget about the traits.
Today I can manually validate that libfoo
's foo.Foo
and libbar
's bar.Bar
donât overlap and that, therefore, my Foo | Bar
union type is safe. But tomorrow libbar
v1.1 is released and it has a brand new dependency on libfoo
and it made Foo
the parent of Bar
. Now Iâm hoping that I matched on Foo
cases before Bar
cases, because all of a sudden that ordering really mattersâŚ
Isnât that just better solved with a general rule of âDonât treat pattern matching as casting unknown typesâ? If youâre casting an instance and donât know itâs concrete type, you will get surprises in tons of ways. Donât even need union types for those surprises either. Iâm confused why union types would get such a special treatment.
If you insist on refusing my opinion that âif a library breaks its API, then it can break codeâ, then you have to only use |
in the alternative case that I mentioned earlier:
(Or with one of the branches being a private type that you can guarantee will be disjoint from any other type, irrespective of what other libraries do.)
This seems extremely reasonable to me. Itâs just the Liskov Substitution Principle: if an operation is valid on A, the operation is valid on an A that also extends B, because extending B doesnât make it any less of an A. Maybe thereâs an operation that is valid on it as a B, but that also doesnât remove the fact that thereâs an operation thatâs valid on it as a A.
If you want to live with OO-style open class hierarchies, you have to live with OO-style principles like LSP, which are entirely self-consistent once you buy into the OO style of doing things. If you try and treat OO-style open class hierarchies like FP-style closed type hierarchies, youâre going to have a bad time, but thatâs probably unavoidable. If you want FP-style closed type hierarchies, use sealed traits and sealed case classes, or the typeclass pattern if you want extensibility.