[swift-evolution] [Pre-Proposal-Discussion] Union Type - Swift 4

Xiaodi Wu xiaodi.wu at gmail.com
Sat Aug 20 13:29:33 CDT 2016

That's good information. I guess a partly implied question which I'm not
entirely sure of would be:

What are the scenarios you've encountered where it actually matters that an
algorithm that works with any P & Q & R must ensure that the argument is
one of a fixed list of concrete types?

Examples of String, Int, Bool call to mind JSON parsing, but in that
scenario I'd expect the fixed list of concrete types would be a constraint
of the parsing logic long before you get to any algorithm that operates on
a P & Q & R.
On Sat, Aug 20, 2016 at 14:09 Matthew Johnson <matthew at anandabits.com>

> On Aug 20, 2016, at 12:27 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> Maybe I'm not getting something. But if you only want T | U | V to expose
> members required by common protocols P, Q, and R, since you know the types
> at compile time, you also know the common protocols. Why wouldn't you just
> write P & Q & R, and if necessary precondition(x is T || x is U || x is V)?
> This isn’t something I personally was advocating for (in fact I
> specifically suggested *not* proposing this would be a better strategy for
> receiving serious consideration of something union-ish).
> However, if we *did* do something like this it would provide a static
> verification that the precondition is met while still exposing the members
> of the common protocol(s) directly.  That is substantially better than
> runtime verification of the precondition or the boilerplate necessary to
> expose provide forwarding members.
> My personal interest in something union-ish is really along the lines of
> syntactic sugar for enums.
> Rather than writing this:
> enum StringOrIntOrBool {
>     case .string(String)
>     case .int(Int)
>     case .bool(Bool)
> }
> func foo(values: [StringOrIntOrBool]) {}
> foo([.string(“hello”), .int(42)])
> I would prefer to just write:
> func foo(values: String | Int | Bool) {}
> foo([“hello”, 42])
> The use case for this I found is in designing DSLs of various kinds where
> you need to accept a heterogenous collection of a specific fixed list of
> concrete types.  The primary concern is to have static verification of the
> possible values provided by the caller without requiring callers to
> manually instantiate cases and without polluting the member types with
> unwanted conformance to a single-use protocol (which would be an
> alternative if we get sealed protocols).
> If we add automatic lifting as Brent suggested that solves the primary
> concern I have.  If we do add that, introducing the union-like notation
> would make sense as convenient syntactic sugar for use in these scenarios
> where the sugar states the intent more clearly and concisely than something
> a contrived name like `StringOrIntOrBool`.  But as I have already stated,
> that is as secondary concern and automatic lifting alone would solve the
> primary (call site / library user) concern.
> Matthew
> On Sat, Aug 20, 2016 at 12:35 Matthew Johnson <matthew at anandabits.com>
> wrote:
>> > On Aug 20, 2016, at 10:36 AM, Haravikk <swift-evolution at haravikk.me>
>> wrote:
>> >
>> >
>> >> On 19 Aug 2016, at 15:38, Xiaodi Wu via swift-evolution <
>> swift-evolution at swift.org> wrote:
>> >>
>> >> Ad-hoc enums have been discussed already, at length, and all the
>> weaknesses touched on then still apply now. For instance, since they're
>> ad-hoc, can you pass an instance of type "Int | String" as an argument if
>> the function expects a "String | Int | Float"? Enums don't have duck typing
>> behavior like that; if your ad-hoc type does, then it's not very much like
>> an enum; if it doesn't, it won't feel much like a union type.
>> >
>> > While ad-hoc enums are certainly similar I don't think that this
>> problem applies to unions; the problem with ad-hoc enums is that while
>> cases may have the same name, the meaning of a case may not be identical,
>> so compatibility is uncertain. For type unions I'd say this isn't an issue;
>> I'd say that yes, String | Int is compatible with String | Int | Float as
>> every possible value can be carried over (whereas the reverse is not true),
>> they're just values of one of several types, so as long as the conversion
>> is possible, it should be fine to pass it on (or rather, repackage it
>> behind the scenes).
>> >
>> >> Moreover, an ad-hoc "String | Int" may look like a union type, but
>> until switching over an instance to cast it, you can't invoke any methods
>> common to String and Int. So it really doesn't feel like a union type at
>> all.
>> >
>> > Could it not do that though? I'd say that a union type should conform
>> to any common protocols that its members conform to; if this can be done in
>> the initial release then great, otherwise it can come later.
>> Conforming to common protocols would be much better than an implicit
>> ad-hoc / duck-typed protocol that simply exposes all common members.  But
>> there is strong opposition to unions, much of which is related to
>> implementation complexity.  It seems to me that the path to having unions
>> or a union-ish feature receiving serious consideration is to demonstrate
>> the value they can offer even with relatively restricted functionality
>> (such as syntactic sugar for enums with implicit lifting).  If that is
>> successful we will have an opportunity to work with them and make a case
>> for enhancements in the future.
>> Also, it won't always possible for a union to conform to a protocol
>> conformed to by all member types.  If the protocol has `Self` requirements
>> in argument position it would not be able to conform and if it has
>> associated type requirements which are bound to different concrete types in
>> the types making up the union it would also not be able to conform.
>> Matthew
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160820/a0b33039/attachment.html>

More information about the swift-evolution mailing list