[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Thorsten Seitz tseitz42 at icloud.com
Sat May 28 12:26:25 CDT 2016


> Am 27.05.2016 um 21:41 schrieb Matthew Johnson <matthew at anandabits.com>:
> 
> 
>> On May 27, 2016, at 2:26 PM, Austin Zheng <austinzheng at gmail.com <mailto:austinzheng at gmail.com>> wrote:
>> 
>> Thanks for all your thoughtful replies.
>> 
>> I'm not really invested in arguing this much further, as it's mainly a stylistic thing that I could live with and also probably a hopeless battle (given how everyone else disagrees).
> 
> I’ve been in the same place with some of the other stylistic battles.  :-)
> 
>> But I would like to address a few final points.
>> 
>> (inline)
>> 
>> On Fri, May 27, 2016 at 12:06 PM, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
>> 
>> 
>> Turning it around, we don’t have to put parentheses around function types and nobody complains about it being problematic even for higher-order functions with several steps before the final result.
>> 
>> Function types have a very regular syntax, especially now that 0066 was accepted (which, I admit, was very controversal itself):
>> 
>> ( <one or more types> ) -> (tuple)
>> or
>> ( <one or more types> ) -> SingleTypeWithNoSpaces
>> or
>> ( <one or more types> ) -> GenericType<All, Spaces, Are, Inside, The, Brackets>
>> 
>> A function type is very easy to visually parse: combine the argument parens, arrow thing, and the single type that it returns. That being said, complex function types are probably the most difficult types to read in a function declaration today, even with this regular structure.
>> 
>> The proposed syntax, which allows arbitrary whitespace outside the context of a delimiter, would require the user to scan the string comprising the existential type expression for a very common sigil in order to locate the endpoint: '=' for variable declarations (which admittedly isn't that bad) or ',' for functions (which is a lot worse). Not to mention the point Joe Groff brought up about a generic function with a generic where clause returning an existential and having everything devolve into a undifferentiated soup of identifiers.
>>  
>> 
>> Does anyone know if users of Ceylon or other languages with the unparenthesized syntax find it problematic?  How would they feel about being required to use parentheses?
>> 
>>> 
>>> We're trying to establish a syntax that will hopefully be used for things significantly more complicated than tuple definitions, which are just a list of types. I think readability is a major concern. Typealiases should be supported, but they shouldn't be required to make the feature useable.
>> 
>> I agree, but I don’t think they would be required to make the feature useable just because parentheses are not required.  If a developer or team thinks they are required for clarity / readability, etc they are free to use them.  This is a style issue that should be addressed by a linter, not the formal syntax of the language.
>> 
>> It is a style issue, but so is (Int) -> T versus Int -> T and a lot of other language details like trailing commas in argument lists, of which the core team seems to feel pretty strongly about.
> 
> Fair enough! :-)
> 
>>  
>> 
>>> 
>>> Finally, wouldn't we need some delimiter for nested existential definitions anyways? Now you have the confusing situation where the outside definition has no delimiters, but the inside ones do:
>>> 
>>> // Why does the inner existential look fundamentally different than the outer one?
>>> // Not to mention, visually parsing the boundaries of this type when you look at it in a function signature
>>> let x : Protocol1, Protocol2, (Protocol 3 where .Foo == Int) where Protocol2.Bar : Baz
>> 
>> Nested existentials are supported not because it would ever be a good idea to actually write them.  They are supported to allow composition of existentials:
>> 
>> 
>> Perhaps then we should only allow existentials to be nested if a typealias is used. Swift is, after all, an opinionated language. If a feature is in the language, it should either be usable directly in an ergonomic way, or it shouldn't be there at all. Having a self-admittedly "bad" way to nest literal existential expressions just for consistency when typealiases are the preferred use case is very unlike Swift.
> 
> I think self-admittedly “bad” is a bit of a stretch.  I do *think* the conventional style would be to match the rest of Swift.  But I’m not *certain* of that.  I could see the style Thorsten posted being conventional and useful in some cases:
> 
> let x : Protocol1 & (Protocol2 where .Bar : Baz) & (Protocol 3 where .Foo == Int)
> 
> I think direct `&` syntax will be most useful when combining two (or maybe three) protocols with no associated types:  `Protocol1 & Protocol2` (whether or not we require parens).  
> 
> Generally I hope we will use type aliases for existentials that are this complex just as we usually bind names to parts of expressions rather than writing large one-liners.
> 
> One issue I’m not sure we have addressed is the case of an existential for a single protocol with an associated type constraint `Protocol where .Foo = Int`.  Before we settle on a syntax we should be sure that this doesn’t introduce any ambiguity.

Austin raised the point (or reminded of Joe’s raising the point) of possible problems when returning constrained existentials from generic functions:

func foo<P, Q>(p: P, q: Q) -> any<Collection where .Element == P> where P: Equatable { … }

would require parentheses when using `&` instead of any<>

func foo<P, Q>(p: P, q: Q) -> (Collection where .Element == P) where P: Equatable { … }

This would even be the case if there was no constraint on P:

func foo<P, Q>(p: P, q: Q) -> (Collection where .Element == P) { … }


An alternative would be to use `with` for existentials instead of `where`:

func foo<P, Q>(p: P, q: Q) -> Collection with .Element == P where P: Equatable { … }
 
But even then this would be more readable either with parentheses (now just as a matter of style) or a line break:

func foo<P, Q>(p: P, q: Q) -> Collection with .Element == P 
	where P: Equatable { … }

-Thorsten


> 
> -Matthew
> 
>>  
>> typealias P3Int = Protocol 3 where .Foo == Int
>> let x : Protocol1, Protocol2, P3Int where Protocol2.Bar : Baz
>> 
>> If you are writing the entire type in a single location I expect the conventional style to be like this:
>> 
>> let x : Protocol1, Protocol2, Protocol 3 where Protocol2.Bar : Baz, Protocol3.Foo == Int
>> 
>> With all associated types constraints in a single `where` clause as we other places they are written in Swift.
>> 
>> Maybe I am wrong about that and a different conventional style would emerge (for example, where clauses clustered with the related protocol).  
>> 
>> But *requiring* parentheses is really orthogonal to the style issue of where and when it is considered *advisable* to use them.
>> 
>> -Matthew
>> 
>>> 
>>> I hope that explains my reasoning.
>>> 
>>> Best,
>>> Austin
>>> 
>>> 
>>>> On May 27, 2016, at 9:28 AM, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
>>>> 
>>>> 
>>>> 
>>>> Sent from my iPad
>>>> 
>>>> On May 27, 2016, at 11:18 AM, Austin Zheng <austinzheng at gmail.com <mailto:austinzheng at gmail.com>> wrote:
>>>> 
>>>>> Here's a strawman idea.
>>>>> 
>>>>> What if we go with '&' and 'where', but we enclose the whole thing in parentheses?
>>>>> 
>>>>> (class & Protocol1 & Protocol2 where .Foo == Int, .Bar : Baz)
>>>>> 
>>>>> There are a couple of reasons I propose this syntax:
>>>>> 
>>>>> - It makes it very clear where the definition of the type begins and ends. I understand people really despise angle brackets, but I really want some way to visually delineate the boundaries of the type. Plus, I imagine it makes syntax a little easier to parse and preemptively forbids some ambiguities.
>>>>> 
>>>>> - It's a structural, not nominal, type, like a tuple, so it uses parens as well. This reserves "<" and ">" for generic types.
>>>>> 
>>>>> - The '&' is easily understood - "Protocol1" *and* "Protocol2". It's also a signal that order doesn't matter - just like how order matters with things that use commas, like argument lists, tuples, and array members, order doesn't generally matter with bitwise or logical 'and' operators.
>>>>> 
>>>>> - If we ever decide to have union types, we have a very elegant third form of nominal type syntax that naturally falls out: (MyClass1 | MyClass2 | MyClass3).
>>>>> 
>>>>> Thoughts?
>>>> 
>>>> Generally in favor.  But I would not require the parentheses.  I believe they would be allowed optionally automatically, just as (Int) is the same as Int (because single element tuples don't exist and the underlying type is used directly instead).  It seems better to leave parentheses up to a matter of style.
>>>> 
>>>> 
>>>>> 
>>>>> Austin
>>>>> 
>>>>> 
>>>>>> On May 27, 2016, at 9:07 AM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>> 
>>>>>> 
>>>>>>> Am 27.05.2016 um 16:54 schrieb Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>>:
>>>>>>> 
>>>>>>>> 
>>>>>>>> On May 27, 2016, at 8:18 AM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>> 
>>>>>>>> Personally I think `&` is more lightweight (and it is established in other languages like Ceylon and Typescript) and `where` is more expressive (and established in Swift for introducing constraints), so I would stay with these.
>>>>>>> 
>>>>>>> I agree.  If we can make `&` with `where` work syntactically it would be nice to go in this lighter weight direction.  If we decide to do that the question then becomes what to do with `protocol`.  Would it be feasible to replace it with `&` in Swift 3 if we decide on that direction?
>>>>>> 
>>>>>> Yep. `protocol` should be replaced with `&` in that case.
>>>>>> 
>>>>>> -Thorsten
>>>>>> 
>>>>>> 
>>>>>>> 
>>>>>>>> 
>>>>>>>> -Thorsten
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> Am 27.05.2016 um 14:34 schrieb Vladimir.S <svabox at gmail.com <mailto:svabox at gmail.com>>:
>>>>>>>>> 
>>>>>>>>> Btw, in case we have `where` keyword in syntax related to types/protocols (when defining constrains. and not some symbol like '>>'.. don't know, for example), why we can't have 'and' keyword also when discuss the syntax of type/protocol conjunction?
>>>>>>>>> I.e.
>>>>>>>>> 
>>>>>>>>> let x: P and Q
>>>>>>>>> let x: P and Q where P.T == Q.T
>>>>>>>>> let x: P and Q and R
>>>>>>>>> 
>>>>>>>>> or, for consistency, as I understand it, we should have
>>>>>>>>> let x: P & Q >> P.T == Q.T
>>>>>>>>> 
>>>>>>>>> On 27.05.2016 11:55, Thorsten Seitz via swift-evolution wrote:
>>>>>>>>>> We could just write
>>>>>>>>>> 
>>>>>>>>>> let x: P & Q
>>>>>>>>>> instead of
>>>>>>>>>> let x: Any<P, Q>
>>>>>>>>>> 
>>>>>>>>>> let x: Collection where .Element: P
>>>>>>>>>> instead of
>>>>>>>>>> let x: Any<Collection where .Element: P>
>>>>>>>>>> 
>>>>>>>>>> let x: P & Q where P.T == Q.T
>>>>>>>>>> instead of
>>>>>>>>>> let x: Any<P, Q where P.T == Q.T>
>>>>>>>>>> 
>>>>>>>>>> let x: P & Q & R
>>>>>>>>>> instead of
>>>>>>>>>> let x: Any<P, Q, R>
>>>>>>>>>> 
>>>>>>>>>> let x: Collection
>>>>>>>>>> instead of
>>>>>>>>>> let x: Any<Collection>
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> This would avoid the confusion of Any<T1, T2> being something completely
>>>>>>>>>> different than a generic type (i.e. order of T1, T2 does not matter whereas
>>>>>>>>>> for generic types it is essential).
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> -Thorsten
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>>> Am 26.05.2016 um 20:11 schrieb Adrian Zubarev via swift-evolution
>>>>>>>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>>:
>>>>>>>>>>> 
>>>>>>>>>>> Something like |type<…>| was considered at the very start of the whole
>>>>>>>>>>> discussion (in this thread
>>>>>>>>>>> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160502/016523.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160502/016523.html>>),
>>>>>>>>>>> but it does not solve the meaning of an existential type and also might
>>>>>>>>>>> lead to even more confusion.
>>>>>>>>>>> 
>>>>>>>>>>> From my perspective I wouldn’t use parentheses here because it looks more
>>>>>>>>>>> like an init without any label |Type.init(…)| or |Type(…)|. I could live
>>>>>>>>>>> with |Any[…]| but this doesn’t look shiny and Swifty to me. Thats only my
>>>>>>>>>>> personal view. ;)
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>> --
>>>>>>>>>>> Adrian Zubarev
>>>>>>>>>>> Sent with Airmail
>>>>>>>>>>> 
>>>>>>>>>>> Am 26. Mai 2016 bei 19:48:04, Vladimir.S via swift-evolution
>>>>>>>>>>> (swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>) schrieb:
>>>>>>>>>>> 
>>>>>>>>>>>> Don't think {} is better here, as they also have "established meaning in
>>>>>>>>>>>> Swift today".
>>>>>>>>>>>> 
>>>>>>>>>>>> How about just Type(P1 & P2 | P3) - as IMO we can think of such
>>>>>>>>>>>> construction as "creation" of new type and `P1 & P2 | P3` could be treated
>>>>>>>>>>>> as parameters to initializer.
>>>>>>>>>>>> 
>>>>>>>>>>>> func f(t: Type(P1 & P2 | P3)) {..}
>>>>>>>>>>>> 
>>>>>>>>>>>> 
>>>>>>>>>>>> On 26.05.2016 20:32, L. Mihalkovic via swift-evolution wrote:
>>>>>>>>>>>> > How about something like Type{P1 & P2 | P3} the point being that "<...>" has an established meaning in Swift today which is not what is expressed in the "<P1,P2,P3>" contained inside Any<P1, P2,P3>.
>>>>>>>>>>>> >
>>>>>>>>>>>> >> On May 26, 2016, at 7:11 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>> wrote:
>>>>>>>>>>>> >>
>>>>>>>>>>>> >>
>>>>>>>>>>>> >>> on Thu May 26 2016, Adrian Zubarev <swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>> wrote:
>>>>>>>>>>>> >>>
>>>>>>>>>>>> >>> There is great feedback going on here. I'd like to consider a few things here:
>>>>>>>>>>>> >>>
>>>>>>>>>>>> >>> * What if we name the whole thing `Existential<>` to sort out all
>>>>>>>>>>>> >>> confusion?
>>>>>>>>>>>> >>
>>>>>>>>>>>> >> Some of us believe that “existential” is way too theoretical a word to
>>>>>>>>>>>> >> force into the official lexicon of Swift. I think “Any<...>” is much
>>>>>>>>>>>> >> more conceptually accessible.
>>>>>>>>>>>> >>
>>>>>>>>>>>> >>>
>>>>>>>>>>>> >>> This would allow `typealias Any = Existential<>`. * Should
>>>>>>>>>>>> >>> `protocol A: Any<class>` replace `protocol A: class`? Or at least
>>>>>>>>>>>> >>> deprecate it. * Do we need `typealias AnyClass = Any<class>` or do we
>>>>>>>>>>>> >>> want to use any class requirement existential directly? If second, we
>>>>>>>>>>>> >>> will need to allow direct existential usage on protocols (right now we
>>>>>>>>>>>> >>> only can use typealiases as a worksround).
>>>>>>>>>>>> >>
>>>>>>>>>>>> >> --
>>>>>>>>>>>> >> Dave
>>>>>>>>>>>> >>
>>>>>>>>>>>> >> _______________________________________________
>>>>>>>>>>>> >> swift-evolution mailing list
>>>>>>>>>>>> >> swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto: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>
>>>>>>>>>>>> > _______________________________________________
>>>>>>>>>>>> > swift-evolution mailing list
>>>>>>>>>>>> > swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto: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>
>>>>>>>>>>>> >
>>>>>>>>>>>> _______________________________________________
>>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto: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>
>>>>>>>>>>> 
>>>>>>>>>>> _______________________________________________
>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org> <mailto: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>
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> _______________________________________________
>>>>>>>>>> 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>
>>>>>>>> _______________________________________________
>>>>>>>> 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>
>>>>>> _______________________________________________
>>>>>> 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>
>> 
>> 
> 

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


More information about the swift-evolution mailing list