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). But I would like to address a few
final points.
(inline)
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,
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.
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 : BazNested 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.
···
On Fri, May 27, 2016 at 12:06 PM, Matthew Johnson <matthew@anandabits.com> wrote:
typealias P3Int = Protocol 3 where .Foo == Int
let x : Protocol1, Protocol2, P3Int where Protocol2.Bar : BazIf 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 == IntWith 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,
AustinOn May 27, 2016, at 9:28 AM, Matthew Johnson <matthew@anandabits.com> > wrote:
Sent from my iPad
On May 27, 2016, at 11:18 AM, Austin Zheng <austinzheng@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@swift.org> wrote:
Am 27.05.2016 um 16:54 schrieb Matthew Johnson <matthew@anandabits.com>:
On May 27, 2016, at 8:18 AM, Thorsten Seitz via swift-evolution < > swift-evolution@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@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 Ror, for consistency, as I understand it, we should have
let x: P & Q >> P.T == Q.TOn 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@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@swift.org>>>:Something like |type<…>| was considered at the very start of the whole
discussion (in this thread
<
[swift-evolution] [Pitch] merge types and protocols back together with type<Type, Protocol, ...>
>),
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 AirmailAm 26. Mai 2016 bei 19:48:04, Vladimir.S via swift-evolution
(swift-evolution@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@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@swift.org <mailto:swift-evolution@swift.org > <swift-evolution@swift.org>>> wrote:
>>
>>
>>> on Thu May 26 2016, Adrian Zubarev <swift-evolution@swift.org < > mailto:swift-evolution@swift.org <swift-evolution@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@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@swift.org>>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@swift.org>>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org
<swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution