Sent from my iPhone

> On Jun 5, 2016, at 6:41 PM, Matthew Johnson <[email protected]> wrote:
> 
> 
> 
> Sent from my iPad
> 
>> On Jun 5, 2016, at 6:20 PM, Douglas Gregor <[email protected]> wrote:
>> 
>> 
>>> On May 18, 2016, at 12:35 AM, Austin Zheng <[email protected]> wrote:
>>> 
>>> I've put together a considerably more detailed draft proposal, taking into 
>>> account as much of Matthew's feedback as I could. You can find it below:
>>> 
>>> https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md
>>> 
>>> Since there is no chance this will come up for review anytime soon, I 
>>> expect to make significant revisions to it over the next month or so. Any 
>>> feedback would be greatly appreciated.
>> 
>> This is very much Swift 4 territory, but I can’t help myself… so…
>> 
>> The actual feature description is spread out through this very long 
>> document, with user-facing ideas (e.g., using “anonymous associated types”) 
>> intermixed with deeper technical details (existential type equivalence), so 
>> it’s very daunting to read. Please bring the user-facing features to the 
>> front (“Proposed Solution”) with examples, and save the deeper technical 
>> details for “Detailed Design”. You want more readers to make it through the 
>> part that affects them.
>> 
>> Shortcut 'dot' notation: If there is only one protocol with associated types 
>> specified in the requirements, and there are no nested Any<...> requirements 
>> with where clauses of their own, that protocol's name can be omitted from 
>> the whereclause constraints:
>> 
>> // Okay
>> // Would otherwise be Any< ~ where Collection.Element == Int>
>> let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> 
>> where .Element == Int>
>> 
>> // NOT ALLOWED
>> // Both Collection and OptionSetType have associated types.
>> let b : Any<Collection, OptionSetType where .Element == Int>
>> FWIW, I think “.Element == Int” should be the only syntax. In generic 
>> signatures, if you have two different protocols with same-named associated 
>> types, and a given type parameter (or associated type) conforms to both 
>> protocols, the associated types are (implicitly) made equivalent via an 
>> inferred same-type constraint. So there’s no reason to introduce the 
>> “Collection.Element == Int” syntax, because the “Collection” part is 
>> basically irrelevant.
>> 
>> Once existentials have been suitably enhanced, there is a strong analogy 
>> between an existential and a generic signature with a single type parameter 
>> that you can’t name. An existential Any<Collection where .Element : 
>> Equatable> has most of the same characteristics as a generic something with 
>> the signature <T : Collection where T.Element : Equatable>. Specifically, 
>> the sections on “Existential type equivalence”, “Ordering”, “Real types to 
>> anonymous associated types”, “Anonymous associated types to real types”. 
>> could be reduced to a few small, simple examples and a mention of the 
>> analogous behavior of generics. It will be far easier to explain this way, 
>> and readers don’t need to get immersed in the details. Where there are 
>> differences vs. generics, that’s important to point out.
>> 
>> “Associated typealias rewriting”: this also falls out of the equivalence 
>> with generics + SE-0092.
>> 
>> “Associated types and member exposure”: you don’t make the point that it 
>> only makes sense to refer to the associated types of a let constant; a var 
>> could change its type dynamically, which would invalidate the typing rules. 
>> Did you consider just using “x.dynamicType” in the type grammar for this? 
>> It’s more general, in that you can refer to associated types but also talk 
>> about the dynamic type of “x” itself, e.g.,
>> 
>>      let x: Equatable = …
>>      let y: Equatable = …
>>      if let yAsX = y as? x.dynamicType { … x == yAsX … }
>> 
>> which is (almost?) as powerful as a general “open” expression.
>> 
>> I’m not a fan of the “anonymous associated types” terminology: these are 
>> associated types of a type of some runtime-defined value. The only thing 
>> “anonymous” about them is that it’s harder to spell the base type; 
>> otherwise, they’re just like associated types of a generic type parameter. 
>> Again, the generics analogy is strong here.
>> 
>> FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve 
>> described here. Also, remember that a method of a protocol extension 
>> essentially opens “Self”, so we already have one way to open an existential 
>> (and that’s probably enough).
>> 
>> I was a little surprised you didn’t point out that AnyObject could become
>> 
>>      typealias AnyObject = Any<class>
>> 
>> or give the nice “AnyCollection” syntax:
>> 
>>      typealias AnyCollection<T> = Any<Collection where .Element == T>
>> 
>> the latter of which is fairly important, because it gives nice syntactic 
>> sure to one of the most highly-requested features [*]. I’d suggest having 
>> that example very, very early.
>> 
>>      - Doug
>> 
>> [*] That generally comes in as “Swift should have parameterized protocols…”
> 
> Great feedback here Doug.
> 
> FWIW, we also occasionally get "Swift should have parameterized protocols" in 
> the context of multiple conformances by the same concrete type (as in things 
> like ConvertibleTo<T> protocol).

I know. From the bugs I've seen it's at least 10x as many requests for "any 
collection of some element type" as for any actually reason why one would need 
parameterize a protocols. 

  - Doug


> 
> 
>> 
>>> 
>>> Austin
>>> 
>>>> On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <[email protected]> 
>>>> wrote:
>>>> 
>>>> 
>>>>> On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <[email protected]> 
>>>>> wrote:
>>>>>>>  
>>>>>>> 
>>>>>>>> 
>>>>>>>> Within the angle brackets are zero or more 'clauses'. Clauses are 
>>>>>>>> separated by semicolons. (This is so commas can be used in where 
>>>>>>>> constraints, below. Better ideas are welcome. Maybe it's not 
>>>>>>>> necessary; we can use commas exclusively.)
>>>>>>> 
>>>>>>> I’m not a fan of the semicolon idea.  I don’t see any reason for this.  
>>>>>>> The `where` keyword separates the protocol list from the constraints 
>>>>>>> just fine.  The list on either side should be able to use commas with 
>>>>>>> no problem (or line breaks if that proposal goes through).
>>>>>> 
>>>>>> I'm leaning towards getting rid of the commas, but would like to write 
>>>>>> out a few 'dummy' examples to see if there are any readability issues 
>>>>>> that arise. 
>>>>> 
>>>>> Replaced with what?  Whitespace separation?  I suppose that might work 
>>>>> for the protocol list but it feels inconsistent with the rest of Swift.  
>>>>> Commas plus (hopefully) the alternative of newline seem like the right 
>>>>> direction to me.
>>>> 
>>>> Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the 
>>>> semicolons and use commas. I've come to the conclusion that there are no 
>>>> readability issues, protocol<> already uses commas, and semicolons used in 
>>>> this manner don't have a precedent anywhere else in the language.
>>>>  
>>>>> 
>>>>>>>> 
>>>>>>>> There are five different possible clauses:
>>>>>>>> 
>>>>>>>> 'class'. Must be the first clause, if present. Places a constraint on 
>>>>>>>> the existential to be any class type. (Implies: Only one can exist. 
>>>>>>>> Mutually exclusive with class name clause.)
>>>>>>>> 
>>>>>>>> (In the future a follow-up proposal should add in 'struct' or 'value' 
>>>>>>>> as a counterpart.)
>>>>>>> 
>>>>>>> If we’re going to allow `struct` we should also allow `enum`.  `value` 
>>>>>>> would allow either of those.
>>>>>> 
>>>>>> Of course. A future proposal can allow list members to discuss the exact 
>>>>>> details as to how struct, value, or enum specifiers should work. 
>>>>> 
>>>>> Yep, agree.  Just mentioning that if we’re going to reference it we 
>>>>> should not leave obvious holes in what would be considered.  :)
>>>> 
>>>> Absolutely.
>>>>  
>>>>> 
>>>>>>>> 
>>>>>>>> Class name. Must be the first clause, if present. (Implies: Only one 
>>>>>>>> can exist. Mutually exclusive with 'class'.) Places a constraint on 
>>>>>>>> the existential (not really an existential anymore) to be an instance 
>>>>>>>> of the class, or one of its subclasses.
>>>>>>> 
>>>>>>> It is still be an existential if it includes protocol requirements that 
>>>>>>> the class does not fulfill.  For example, you might have Any<UIView, 
>>>>>>> SomeProtocol> where UIView does not conform to SomeProtocol, but 
>>>>>>> various subclasses do.
>>>>>> 
>>>>>> Fair enough. (I don't think the way things work would be affected.)
>>>>>>  
>>>>>>> Your proposal doesn’t discuss composing Any in the way that Adrian’s 
>>>>>>> did like this:
>>>>>>> 
>>>>>>> typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
>>>>>>> Any<AnotherProtocol, Foo>
>>>>>> 
>>>>>> I didn't think it needed to be discussed. An Any<...> existential type 
>>>>>> is a type 'expression' just like any other, and should be allowed to 
>>>>>> participate in other Any<...>s.
>>>>>>  
>>>>>>> 
>>>>>>> I like the idea of composition as it allows us to factor out 
>>>>>>> constraints.  If we are going to do that we should allow a class to be 
>>>>>>> specified in the composition as long is it is a subclass of all class 
>>>>>>> requirements of Any types it composes.  For example, this should be 
>>>>>>> allowed:
>>>>>>> 
>>>>>>> typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>
>>>>>>> 
>>>>>>> This is still one class requirement for Bar, it just refines the class 
>>>>>>> requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.
>>>>>> 
>>>>>> This is a good point. There should be clarification as to how special 
>>>>>> cases of Any<...> used in another Any<...> behave. For example, like you 
>>>>>> said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. 
>>>>>> This will go into any proposal that emerges from the discussion.
>>>>> 
>>>>> Yes, this is why we need to discuss Any composition.  There are also 
>>>>> cases of incompatible associated type constraints which need to be 
>>>>> rejected (such as composing two Any’s where one has Element == String and 
>>>>> another has Element == Int).
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>>> Example: Any<UIViewController; UITableViewDataSource; 
>>>>>>>> UITableViewDelegate>
>>>>>>>> "Any UIViewController or subclass which also satisfies the table view 
>>>>>>>> data source and delegate protocols"
>>>>>>>> Dynamic protocol. This is entirely composed of the name of a protocol 
>>>>>>>> which has no associated types or Self requirement.
>>>>>>>> Example: Any<CustomStringConvertible; BooleanType>
>>>>>>>> "Any type which conforms to both the CustomStringConvertible and 
>>>>>>>> BooleanType protocols"
>>>>>>>> 
>>>>>>>> I'm going to use 'static protocol' to refer to a protocol with 
>>>>>>>> associated types or self requirements. Feel free to propose a more 
>>>>>>>> sound name.
>>>>>>>> 
>>>>>>>> Self-contained static protocol, simple. This is composed of the name 
>>>>>>>> of a static protocol, optionally followed by a 'where' clause in which 
>>>>>>>> the associated types can be constrained (with any of the three basic 
>>>>>>>> conformance types: subclassing, protocol conformance, or type 
>>>>>>>> equality). Associated types are referred to with a leading dot.
>>>>>>> 
>>>>>>> Please do not introduce terms “dynamic protocol” and “static protocol”. 
>>>>>>>  We want to support existentials of protocols that have self or 
>>>>>>> associated type requirements.  The dynamic vs static distinction is a 
>>>>>>> limitation of the current implementation of Swift and doesn’t make 
>>>>>>> sense for the long term vision.
>>>>>> 
>>>>>> I'm not trying to introduce new terms, these are just placeholders. At 
>>>>>> the same time "protocols with self or associated type requirements" is 
>>>>>> cumbersome to work with and it would be nice for someone to come up with 
>>>>>> a descriptive term of art for referring to them.
>>>>> 
>>>>> I agree that a better term would be useful.  In the meantime, I would 
>>>>> prefer something like “trivial” and “nontrivial” protocols.
>>>> 
>>>> I've decided to just use the full name until the community comes up with 
>>>> better names. Clarity is preferable to brevity in this case.
>>>>  
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>>> 
>>>>>>>> Example: Any<Collection where .Generator.Element : NSObject, 
>>>>>>>> .Generator.Element : SomeProtocol>
>>>>>>>> "Any type that is a Collection, whose elements are NSObjects or their 
>>>>>>>> subclasses conforming to SomeProtocol.”
>>>>>>> 
>>>>>>> Swift does not allow disjunction of requirements.  Only conjunctions 
>>>>>>> are supported.  That means the correct reading is:
>>>>>>> 
>>>>>>> "Any type that is a Collection, whose elements are NSObjects and their 
>>>>>>> subclasses conforming to SomeProtocol.”
>>>>>> 
>>>>>> Yes, that is what I meant. "whose elements are (NSObjects or their 
>>>>>> subclasses) conforming to SomeProtocol”.
>>>>> 
>>>>> Ok, good.  Wasn’t quite clear to me.
>>>> 
>>>> Yes, the verbiage will need to be clearer in the future. That sentence 
>>>> could be ambiguously parsed.
>>>>  
>>>>> 
>>>>>>>> 
>>>>>>>> Bound static protocol. This is the same as a self-contained static 
>>>>>>>> protocol, but with a leading "<name> as " which binds the protocol to 
>>>>>>>> a generic typealias. The name can be then be used in subsequent 
>>>>>>>> clauses to build constraints.
>>>>>>>> 
>>>>>>>> Example: Any<T as Collection; IntegerLiteralConvertible where 
>>>>>>>> .IntegerLiteralType == T.Element>.
>>>>>>>> "Any type that is a Collection, and also can be built from an integer 
>>>>>>>> literal, in which the collection elements are the same type as the 
>>>>>>>> type of the integer used for the integer literal conformance.”
>>>>>>> 
>>>>>>> I’m not sure about this, but if we’re going to do it it should be the 
>>>>>>> other way around: `Collection as T` with the alias after the name of 
>>>>>>> the protocol.  
>>>>>> 
>>>>>> I like this, it flows better. "Protocol as T where Protocol.Foo == Int, 
>>>>>> Protocol.Bar : Baz”.
>>>>> 
>>>>> Why did you introduce an alias here and then not use it?  Did you mean 
>>>>> "Protocol as T where T.Foo == Int, T.Bar : Baz"
>>>> 
>>>> Another result of rushing to compose an email. Sorry!
>>>>  
>>>>> 
>>>>>>  
>>>>>>> You are also using “dot shorthand” here to refer to an associated type 
>>>>>>> of IntegerLiteralConvertible.  I think “dot shorthand” should be 
>>>>>>> limited to cases where there is only one protocol that is getting 
>>>>>>> constrained.  In other cases, we need to be clear about which protocol 
>>>>>>> we are referring to.
>>>>>> 
>>>>>> I borrowed dot shorthand from the generics manifesto. But you are right, 
>>>>>> it should only be allowed if there is one protocol with associated types 
>>>>>> or self requirements clause in the Any<...> construction.
>>>>> 
>>>>> I would actually go further and limit it to one protocol period, and 
>>>>> possibly even to one protocol and no type names (as types can have nested 
>>>>> types and typealiases).  When we allow shorthand it should be immediately 
>>>>> unambiguous what the shorthand references with no need to look at type or 
>>>>> protocol declarations.
>>>> 
>>>> It might be desirable to propose the proposal with no allowance for 
>>>> shorthand, and have the dot shorthand be a smaller follow-up proposal.
>>>>  
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>>> 
>>>>>>>> There will be rules to prevent recursive nesting. For example, if 
>>>>>>>> generic typealiases are allowed, they cannot refer to each other in a 
>>>>>>>> circular manner (like how structs can't contain themeselves, and you 
>>>>>>>> can't create a cyclic graph of enums containing themselves).
>>>>>>>> 
>>>>>>>> How an existential can be used depends on what guarantees are provided 
>>>>>>>> by the clauses. For example, 'Any<Equatable>' can't be used for much; 
>>>>>>>> if there were any methods on Equatable that did not use the associated 
>>>>>>>> types at all you'd be able to call them, but that's about it. However, 
>>>>>>>> 'Any<Equatable where .Self == String>' would allow for == to be called 
>>>>>>>> on instances. (This is a stupid example, since Any<Equatable where 
>>>>>>>> .Self == String> is equivalent to 'String', but there are almost 
>>>>>>>> certainly useful examples one could come up with.)
>>>>>>>> 
>>>>>>>> In order of increasing 'power':
>>>>>>>> Don't constrain any associated types. You can pass around 
>>>>>>>> Any<Equatable>s, but that's about it.
>>>>>>>> Constrain associated types to conform to protocols.
>>>>>>>> Fully constrain associated types.
>>>>>>> 
>>>>>>> I think we need to spell out pretty clearly what members we expect to 
>>>>>>> be available or not available.  This section probably needs the most 
>>>>>>> design and elaboration.  
>>>>>>> 
>>>>>>> For example, we probably can’t access a member who uses an associated 
>>>>>>> type as an input unless it is constrained to a specific type.  On the 
>>>>>>> other hand output types probably don’t need to limit access to a 
>>>>>>> member.  However, if the output type is Self or an associated type the 
>>>>>>> visible signature would have an output type which has the relevant 
>>>>>>> constraints of the existential applied, but no more.  In some cases 
>>>>>>> this means the output type would simply be Any.
>>>>>> 
>>>>>> Absolutely. This is vaguely what I had in mind but I wanted to get 
>>>>>> something down first. Thanks for thinking through some of the 
>>>>>> implications :).
>>>>> 
>>>>> That’s what I thought.  Just wanted to start the process of elaborating 
>>>>> expectations.
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>> Where this really gets tricky is for compound types like functions, 
>>>>>>> generic types, etc.  Working out the details in these cases is pretty 
>>>>>>> complex.  I will defer to Doug on whether it is best to just defer 
>>>>>>> those cases to the future, leave them up to the implementer, or try to 
>>>>>>> work out all of the relevant details in the proposal (in which case we 
>>>>>>> probably need a type system expert to help!).
>>>>>> 
>>>>>> Yes, exactly! For example, can Any<...> existentials involving protocols 
>>>>>> with associated types or self requirements be used within generic 
>>>>>> function or type definitions? Maybe there's an argument that existential 
>>>>>> types of this nature are redundant if you have access to generics (e.g. 
>>>>>> defining a property on a generic type that is a Collection containing 
>>>>>> Ints; you should be able to do that today). On the other hand, maybe 
>>>>>> there are use cases I haven't thought of…
>>>>> 
>>>>> I see no reason they shouldn’t be.  They are not redundant at all.  For 
>>>>> example, you may want to store instances in a heterogeneous collection.  
>>>>> You need existentials to do that.
>>>>> 
>>>>> A simple example of what I was referring to there is something like this:
>>>>> 
>>>>> protocol P {
>>>>>     associatedtype Foo
>>>>> 
>>>>>     func bar(callback: (Foo) -> ())
>>>>> }
>>>>> 
>>>>> In other words, types in the signature of a protocol member are complex 
>>>>> types that reference Self or associated types.  I think you really need a 
>>>>> formal understanding of the type system to understand how to expose these 
>>>>> members through a constrained existential.  We can probably understand 
>>>>> the expected behavior in some of the simpler cases on a case by case 
>>>>> basis, but that approach doesn’t scale at all and is arbitrary.  If 
>>>>> they’re going to be supported an expert is going to need to be involved 
>>>>> in the design.
>>>> 
>>>> Yes. I have some ideas regarding this topic.
>>>>  
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>> One area you didn’t touch on is “opening” the existential?  Is that out 
>>>>>>> of scope for this proposal?  That would be fine with me as this 
>>>>>>> proposal is already taking on a lot.  But if so, you should mention 
>>>>>>> something about future directions as it is pretty closely related to 
>>>>>>> this proposal.
>>>>>> 
>>>>>> Yes, existential opening is explicitly separate from this (although I 
>>>>>> wanted to mention it in the section where I talk about how 
>>>>>> Any<Equatable> is not very useful). But you are absolutely right, this 
>>>>>> proposal should discuss how it wants to interact with possible future 
>>>>>> directions.
>>>>>>  
>>>>>>> 
>>>>>>> Another area you didn’t touch on is whether Any constructs (and 
>>>>>>> typealiases referring to them) should be usable as generic constraints. 
>>>>>>>  I would expect this to be possible but I think we need to spell it out.
>>>>>> 
>>>>>> I'm hoping for community input. This is a tricky subject, and at some 
>>>>>> point we'll bump into implementation limitations.
>>>>> 
>>>>> I don’t think it’s too tricky.  You can just unpack the constraints of 
>>>>> the Any into the list of generic constraints.  Maybe I’m missing 
>>>>> something, but I don’t think so.
>>>>> 
>>>>>>  
>>>>>>> 
>>>>>>> -Matthew
>> 
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to