[swift-evolution] Union instead of Optional

Michael Peternell michael.peternell at gmx.at
Mon May 16 10:29:55 CDT 2016


> Am 16.05.2016 um 12:07 schrieb Austin Zheng via swift-evolution <swift-evolution at swift.org>:
> 
> Precisely. To me unions are to enums with associated type the same way tuples are to structs. One is named, has well-defined semantics, can conform to protocols, can have complex internal structure, can have methods, etc. The other is ad-hoc and lightweight, easy to define at the site of use, best suited for simple purposes, has special syntax to support it. 
> 
> Even if we can extend tuples in the future, though, I wouldn't want structs to go away. When exceeding some level of complexity structs are just more explicit than tuples, and therefore easier to understand.
> 
> Finally, please note that Ceylon is a pervasively object-oriented language with a single root superclass. Neither of those is true for Swift, which chooses to solve a lot of problems in a different (and I would argue, superior) way. So solutions that might work well in Ceylon might not be suited for Swift, at least not without modification, and vice versa. The core team could certainly have chosen to model Swift's type system after that of e.g. Scala, but they chose not to, and I think they did so for good reason.

Swift has a root class, it is called SwiftObject and it's visible from Objective-C ;) Just thinking about it.. it makes sense: the whole reference-counting-stuff has to live somewhere in the object, and there has to be some kind of isa-pointer to allow for subclassing (and Objective-C interoperability). So there is some common behavior regarding all classes defined from Swift (they all implement retain, release, autorelease, isEqual:, class, respondsToSelector:, the whole NSObject-protocol...) => what I want to express: Java has a root-class, C++ has no root class - that's uncontested; but Swift is somewhere in-between IMHO.

And for enums and unions.. I think they are different. enums are "sum-types" and unions are... well... "union-types", when you think of data types as sets. E.g. if A, B are data types (sets), then an enum that can be either anA(A) or aB(B) can be thought of as the set A+B (and if A \intersect B \not\eq \emptyset, you can think of this as (0,A)\union (1,B) ). A union B is not the same. In C, the unions are not even safe, because they are not discriminated in any way. In Swift they make an isomorphic set if A and B are disjoint. If A=String and B=Int, A `enum` B is isomorphic to A `union` B (sorry for abusing Haskell syntax :-/ ). But if A=Iterable<String> and B=Array<String>, they are not the same, because now A is a superset of B. So A `enum` B is a strict superset of A `union` B (== A). I can already imagine weird bugs coming to the surface from this distinction, all of which can be solved by disallowing union types altogether.

Therefore I think that unions are not worth the trouble at all. And they are even possible right now: Instead of writing

    union MyUnion { Int, String }
    // or typealias MyUnion = (Int | String) // ?

you'd have to write

    protocol MyUnion {}
    extension Int: MyUnion {}
    extension String: MyUnion {}

The two definitions are basically equivalent, and the second is already valid Swift.

I have used unions a few times in C though, e.g. for converting from void* to int or stuff like that, at a time when I didn't know about reinterpret_cast (Swift: unsafeBitcast) yet. The few occasions where they are use in the C standard library, they are always discriminated, like a union between a struct(int type, float foo, ...) and another struct(int type, char bar[20], ...) where the type parameter is in both values and can be used to distinguish them. In Swift they are always distinguishable because of runtime type information (except for the case described above), so you can as well just use an enum and make that information obvious. To unpack the information you can use `if let` when dealing with a union, and you can use `if case` or `switch` when dealing with an enum. I don't see how unions would be more convenient to be worth the trouble. Swift unions would be like (C unions + RTTI (runtime type information)), with RTTI replacing the discriminator element of enums, except when the types overlap. So I doubt that anyone will find a decent (realistic!) use-case that is not just as easily (and conveniently) implemented using enums. I think I just proved that there is no such use case ;) The best way to convince me otherwise would be to provide a realistic piece of example code.

Regards,
Michael



More information about the swift-evolution mailing list