Thank you for this proposal!
Please let me address the question of explicit syntax for existentials. Your proposal states:
So far, existentials are the only built-in abstraction model (on par with generics and opaque types) that doesn't have its own idiosyncratic syntax; they are spelled as the bare protocol name or a composition thereof.
The Swift style guide says that protocols should have a name that makes it clearly a protocolâan adjective or gerundive describing the capability or behavior represented by the protocol requirement: Hashable
, Comparable
, etc. This is not compiler-enforced syntax, but there is seldom any confusion about what is a protocol and what isn't.
the syntax strongly suggets that the protocol as a type and the protocol as a constraint are one thing
Existing Swift gives constraints an explicit syntax; it's clear they are not the same thing as using a protocol as a type. Consider:
protocol Processable {}
protocol Usable {}
func process<T: Processable>(_ item: T) -> Usable
Processable
is being used as a constraint, and Usable
is not. It's not ambiguous.
If we change this to:
func process<T: Processable>(_ item: T) -> any Usable
That's more ambiguous, not less, because now it makes sound like Usable
is a constraint on any
type that gets returned by the function, just like View
is a constraint on some View
that gets returned by a SwiftUI body. However this function won't return just "any" concrete type that conforms to Usable
, it will always return the same exact type: Usable
(the existential).
in practice, they serve different purposes, and this manifests most confusingly in the "Protocol (the type) does not conform to Protocol (the constraint)" paradox.
I agree it's confusingly worded, but it's not a paradox. There are other ways to explain this, where it makes perfect sense.
This could be qualified as a missing feature in the language; the bottom line is that the syntax is tempting developers when it should be contributing to weighted decisions.
I don't think syntax "tempts" people. It's just syntax. We should avoid the temptation to use syntax to prescribe behaviors.
If we are seeing people avoiding using certain language features, or being confused by them, I think it means we could do a better job of documenting the language & educating Swift developers about how the compiler and runtime work. I'd note that @xwu and their team has done a really excellent job of this lately, and as well there's a lot more resources available for learning the intricacies of Swift than there were a few years back.
Nonetheless, many Swift devs are still operating from assumptions we got from the "protocol-oriented programming" talk at WWDC five+ years ago, a talk which hardly anyone fully understood (at the time). Most of us walked away from it thinking "use structs and protocols more" but all the talk about static vs. dynamic dispatch sailed right over our heads.
I'm not saying the syntax can't be improved, but we should be careful about why we're doing it lest we make the problem actually worse and end up with a burdensome, overbearing language.
We should try to come up with a simpler metaphor to better express the concept of a "protocol-type object" and ultimately the dynamic vs. static dichotomy that is the elephant in the room.
Because using existential types is syntactically lightweight in comparison to using other abstractions, and similar to using a base class, the more possibilities they offer, the more users are vulnerable to unintended or inappropriate type erasure by following the path of initial least resistance.
I don't think this is why people are avoiding using generics and PATs. It has nothing to do with some code being easier to type.
The reason some people stick to using existentials is because when they tried to write code using PATs, they ran into compiler errors that were not straightforward to resolve, and then when they tried to use generics, they couldn't because you can't pass an existential into a generic parameter (unless it's an @objc
existential without static requirements, a little-known exception to that rule although as of last summer it's in the official documentation).
By removing many of those errors, your proposal will unblock more people from using these featuresâno syntax change needed.
Now, if the decision to change the existing syntax in a breaking way is made, I hope we can do it in a way that helps clarify the language and make it easier to have conversations about it. I might suggest:
- the new syntax should be a single word that could be used interchangeably with "existential" or "protocol-type object" in a sentence, while not sounding confusing
- the new syntax could ideally make it easier for Swift devs to recognize when static vs. dynamic type information is relied on
- the new syntax should make it unambiguous when reference semantics are likely incurred
Some examples I could think of:
var foo: Proto<Fooable>
var foo: Object<Fooable>
var foo: dynamic Fooable
I like Object<Fooable>
because it makes it obvious that protocol-type objects will incur reference semantics just like any other object (yes there are some exceptions but they're basically just a compiler optimization when the object is tiny). It also makes it obvious that we're dealing with a concrete type that allows no further generic specialization, and from which no further static type information can be "opened" without dynamic casting or type erasure schemes.
The problem with "dynamic" is that it's too overloaded. Swift already has the attribute declaration modifier dynamic
, which has an explicit definition as "dynamically dispatched using the Objective-C runtime". However @nonobjc
existentials can still be dynamically dispatched, and the attributes @dynamicMemberLookup
and @dynamicCallable
don't seem to be directly related to the Obj. C runtime. So what does "dynamic" really mean? I find it confusing; clarifying this would be great but not sure if it's worth breaking syntax.
Hope this could be helpful to your proposal, and thanks again, really looking forwards to this being in the language. Even if it does still have some limitations, I think it reduces the number of limitations we have to deal with and will encourage people to revisit their attitudes towards PATs. Thanks!