Not quite. A type union would be more like a @frozen enum
than an enum
. That's the rub: when you have a public API that returns a type union, you can't add new stuff without breaking all the callers. There's times that's a perk (e.g. we don't want anyone adding new cases to Result
), but the rest of the time it's a restriction on new API additions to the library.
That would be my objection to the enum FieldKind
example in your linked post. You don't have a case dateField(DateField)
, and now you can't add it (assuming your enum was public API of a library) without breaking callers. A protocol FieldKind
would have been better.
This is an interesting read, I think I'll need more time to mull it over!
One thing that sticks out is that the use of union types in function parameters is very much like the flag argument anti-pattern. Your function implementation ends up being one giant switch, instead of multiple tidy functions.
If that were the case, then we can do the opposite and shrink the surface of every library down to a single function! All you need is this:
func fn(args: Fn1Args(...) | Fn2Args(...) | ...) -> (Fn1Result | Fn2Result | ...)
Switching to a flag argument doesn't decrease surface, it just obscures it. A new overload does increase surface, but no differently than adding a new member to a type union of an existing function's parameter.