[swift-evolution] Value Subtypes and Generalized Enums, a manifesto

Matthew Johnson matthew at anandabits.com
Thu Feb 16 17:26:23 CST 2017


> On Feb 16, 2017, at 5:13 PM, Karl Wagner <razielim at gmail.com> wrote:
> 
> 
>> On 17 Feb 2017, at 00:00, Karl Wagner <karl.swift at springsup.com <mailto:karl.swift at springsup.com>> wrote:
>> 
>> 
>>> On 16 Feb 2017, at 23:44, Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> I have been thinking a lot about enums and value subtyping lately and decided to write down the ideas I’ve been thinking about.  The result is a manifesto-style document that explores a broad landscape of features that could eventually lead to proposals (at the right time, of course).
>>> 
>>> I’m presenting this document to the list now mostly because I am not sure which of these features (if any) might be relevant to ABI stability, particularly with respect to standard library APIs.  I do not wish to distract the list from the focus on Swift 4, phase 1.  Let’s try not to get distracted by exciting ideas that won’t be in scope until at least phase 2.  Feel free to send feedback off list if you’re interested in discussing ideas that may not be relevant to Swift evolution at this time.
>>> 
>>> Because this document covers a pretty broad range of topics it might be a good idea to start a new thread before jumping in to discussion about a specific aspect of it.  Please consider doing that if it is relevant before responding directly to this thread.
>>> 
>>> As this is a relatively large document I am only providing a link: https://gist.github.com/anandabits/5b7f8e3836387e893e3a1197a4bf144d <https://gist.github.com/anandabits/5b7f8e3836387e893e3a1197a4bf144d>
>>> 
>>> To whet your appetite, the topics covered include:
>>> 
>>> * Definition of value subtyping
>>>   * Transitivity of value subtypes
>>>   * Generic supertype constraints
>>> * Axiomatic value subtype relationships
>>> * Enums: Value Subtype Relationships by definition
>>>   * Nominal case types
>>>   * Nominal unions
>>>   * Generic enums and Optional
>>>        * Cases with unbound generic arguments
>>>   * Structural Unions
>>>   * Enum subtypes
>>>   * Inline enum subtypes
>>>   * Inline generic enum subtypes
>>>   * Conditional cases (and GADTs)
>>>   * Inline case types
>>>   * Nominal cases with inline types
>>>   * Case type implementation sharing
>>>        * Shared stored properties
>>>            * Subenum stored properties
>>>        * Shared methods and computed properties
>>> * User-defined case patterns
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> 
>> 
>> 
>> enum IntOrString: Int | String {
>>   case Int
>>   case String
>> }
>> 
>> func takesAnonymousUnion(intOrString: Int | String) {}
>> 
>> Haven’t been through it all, just pointing out that “Structural unions” and anonymous unions have been suggested and rejected before.
>> 
>> - Karl
> 
> (maybe I should elaborate on that, because you do point it out in the document)
> 
> What I mean is that the reason this was rejected before was that people should be using protocols instead, and building semantic contracts. “Int” and “String” may have a method that looks the same but behaves very differently - the union provides no guarantees about _behaviour_. Protocols *do* give you guarantees about behaviour.
> 
> Basically, the correct way to write this (today) is:
> 
> protocol CommonIntAndStringMethods {
>     func doSomething()
> }
> 
> enum IntOrString {
>     case integer(Int)
>     case string(String)
> }
> extension Int: CommonIntAndStringMethods {}
> extension String: CommonIntAndStringMethods {}
> 
> func myFunc(_ x: IntOrString) {
>     let val: CommonIntAndStringMethods
>     if case .integer(let i) = x     { val = i }
>     else if case .string(let s) = x { val = s }
> 
>     val.doSomething()
> }
> 
> What you are proposing looks superficially similar, but isn’t. We call “doSomething” on a single type, with guaranteed same semantics.

Thanks for pointing out this aspect of the prior discussions.  This is great feedback.  This part of the prior discussions had slipped my mind so I didn’t distinguish the unions I am talking about from the union types that some people have asked for in the past.  I’ll update the manifesto to be more clear about this.

The intent is not to expose common methods on the union type at all.  In fact all you could do with a structural union as I am defining it is attempt to downcast (or switch with cast patterns).  These structural unions would be for things such as:

typealias JSONValue = None | Bool | Int | Double | String

Sometimes what we require union types like this.  Today we can define an enum JSONValue bit it is less elegant than it could be.  We don’t get the subtype relationship with Bool, Int, Double, String, etc.  This means we don’t get implicit conversion to JSONValue and we don’t have the ability to downcast from JSONValue.  It also means that my JSONValue is incompatible with your JSONValue.

None of this has anything to do with any operations that might be available on any (or all) of the types making up the union.  I think this makes the implementation much more tractable and avoids the semantic issues you point out.

> 
> - Karl
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170216/963f6b39/attachment.html>


More information about the swift-evolution mailing list