Got it, thanks. Still don’t love the idea of quietly changing the ABI of a bunch of resilient interfaces and relying on other tools to catch it but I suppose the thinking here is that any resilient library author who cares at all about not breaking ABI must already be using such tools?
A variable of opaque type must be bound to a single static type. If you could declare a variable of opaque type without immediately initializing it, you could put the initialization inside an if
statement, where each branch bound the variable to a different concrete type:
func myFunc(arg: Bool) - > some Sequence<Int>
let x: some Sequence<Int>
if arg {
x = SequenceOfOne(42)
} else {
x = [42]
}
return x // concrete type of `x` depends on execution path!
}
IIRC, this could theoretically work if the compiler could statically prove that all execution paths through the function bound x
to the same concrete type, but that’s too complex of a dataflow analysis problem to do regularly.
As I've argued before, this does not seem like a very good idea, at least if we can't get rid of some
everywhere. It seems from the pitch that we can't do that, but I can't really see exactly why those cases are different.
If we are keeping some uses of some
, and also, as you do here, talk about the bare protocol as an "implicit some", I don't think that's a very clear or simple situation. If people will still have to imagine a some
, then what is really gained. I think this change only makes sense if we can completely get rid of the whole concept of some
, and just have any
and bare protocol, where bare protocol doesn't mean specifically some
.
Also, I think the migration argument is very weak, it seems that will only really apply to badly written code, code where implicit any
shouldn't have been used in the first place. That seems like the last thing we want to optimise for.
May I plea for something more than a "future direction", and instead some kind of moral acknowledgment that the compiler shouldn't ship with this pitch but without this shorthand? In my very humble opinion the shorthand should even ship first and become a precondition for this pitch (i.e. the shorthand must be supported, instead of some non-mandatory eventual enhancement).
Forcing users to write the mouthful and arcane weak var delegate: (any MyDelegate)?
is somewhere between inelegant and hostile, considering that the delegate pattern is still quite vivid (and unavoidable when you work with AppKit/UIKit frameworks - you know, the ones you must use until your app has a minimum target OS where SwiftUI catches up).
// A quick reminder:
// Before SE-0335
weak var delegate: MyDelegate?
// SE-0335
weak var delegate: (any MyDelegate)?
// SE-0335 + shorthand 🙏
weak var delegate: any MyDelegate?
I believe @GreatApe’s conclusion totally makes sense. I’m not a fan of simply making codes shorter unless we can achieve extra proof or functionality by doing that. Unfortunately this pitch didn’t seem to have such benefits.
some
and any
, as a pair of keywords, represents two ways and views we can work with the generic type system most of the time. Questions like "whether we want any
or some
for an API" generally help programmers to think in a generic way. On the other hand, when we don’t see the two keywords, we also know clearly that the type is either concrete or having external constraints.
The direction shown by this pitch largely breaks the balance and, despite making rooms for "simple generic codes", may be harmful for language learners and users.
While this is an interesting direction I have some concerns with this pitch. It strongly feels like we would like to revert some
in almost all places and reverting several past proposals on that topic. I'm personally not in favor of such drastic change.
If you recall we want to be able to refer to a protocol as both an existential or the protocol. If that change this would not be possible.
typealias MyP = P // MyP is a protocol alias not an alias for `some P`
typealias AnyP = any P // aliasing the existential
We could also introduce the ability to have several very useful meanings in extensions when using P
, any P
and some P
So it's -1 for me.
I was in favor of this shorthand during the review of SE-0335 and remain so today; but in my view, some P?
(or, with this pitch, just P?
) and any P?
shorthands can be separated out as an orthogonal pitch—indeed, I would be in favor of not requiring parentheses here even if we reject this pitch in its entirety.
It's certainly possible, but my two cents here are that we are not in a good place if that's what it will take.
Put another way, I think the pitch is a very intriguing idea, and it would be a great thing essentially to silently swap the meaning of naked P
from any P
to some P
if it (a) reduces churn during the transition to Swift 6; and (b) even helps to improve the performance of users' code when they're reaching for existential types now without actually requiring it—all (c) without silently causing inadvertent changes in behavior except in the cornerest of corner cases. I think I'm well convinced of (a) and (b), but like @Jumhyn I'm quite worried about (c).
If we don't have confidence that we can migrate entire swaths of code (e.g., all public APIs) without special tools to avoid silently making correct code incorrect, then we're leaning pretty heavily on best-effort heuristics for correctness.
Particularly when it comes to the type system, I think that would fall firmly on the "un-Swifty" side of the spectrum from completeness to correctness that @itaifarber described so well. Yes, there'd be nothing more unsound about the underlying type system, but we'd be basically overlaying a "guess" as to whether a naked P
in Swift 5 code should become any P
or some P
at the level of the migration tool, effectively making the meaning of P
(or, if the tool will error, the validity of using it) heuristically determined.
Yeah this is a good thing to call out. Authors of public libraries who don't know all their clients (and cares about breaking source) will basically have to make the P
-> any P
transformation because any client might be using the declaration in a way that would be invalid with some P
. Perhaps that suggests a narrower rule such as
Bare
P
as shorthand forsome P
is only available in positions that are not exposedpublic
ly.
at least for Swift 6.
It comes to my mind that the switch of implicitness from any
to some
has some impact on DocC - both for internal and public code.
I didn't spend more than a few minutes thinking about it, but I can feel that function prototypes displayed by DocC will become a point of confusion unless we are very careful.
In the imaginary DocC page below, are we talking about existentials, or opaque types? When was this documentation generated? Which language mode does it target? Do I have to run implicit conversions in my mind when it happens that I program Swift 6, and read the doc of a Swift 5 package? What if I don't even know the language mode of the package I use? How can I have a non-ambiguous interpretation of what I read, and which amount of knowledge/experience and energy does it take?
Declaration
func frobnicate(_ x: P1) -> P2
Parameters
etc.
Tangential question: will DocC format explicit some P
in source code to bare P
in the generated doc, or the opposite? Or no formatting at all?
I guess this would be the topic on another pitch… Please pardon me @Joseph_Heck to ping you directly - maybe some members of the Documentation Workgroup have something to say here.
In general I'm in favor of the pitch. I think it makes sense for some
to be the default choice and therefore require the least ceremony. I do share @Jumhyn's concern about the plan for resilient libraries, though:
I feel nervous about relying on usage of the ABI checker to ensure that library owners don't break ABI during the transition because I don't feel confident that the practice is widespread enough. Personally, I'd rather see an exception requiring explicit some
or any
in public
declarations of resilient libraries. With some well worded diagnostics I don't think it would feel that strange.
No problem pinging me, but I don't have any good answers here. I'll need to defer to others in the workgroup - I'm hoping @QuietMisdreavus or @Franklin might have a clearer picture of what metadata is captured in the symbol extraction and flowed through to the end results - either the compacted JSON data structures in the doc archive or the HTML output rendered from it.
I think in either case, eliding it would seem to me that it would still be explicit in the meta-data to the compiler, and that could be reflected out into the metadata that DocC uses - but I don't know what is or isn't included there today to distinguish between any
and some
.
Thank you for not rushing an answer :)
I'm strongly against this because:
-
The introducing of
any
came with the educational benefit of clarifying that a protocol P is not the same as a type P. Forcing the use ofsome
orany
make us deal with it and think carefully about what our intention is when using a protocol as a parameter. I really thought this was the intention of Swift 6. -
Since
some
won't go away we will keep having three different ways of using protocols, where two actually mean the same thing. -
The mandatory use of
any
orsome
also brings clarity at the point of use. You just know that you dealing with a protocol because of these keywords.
In summary, I think eliding some
will just keep a bunch of today problems of using P
as type but just move theses problems to a less harmful place.
Not breaking the ABI matters so little to Swift code that builds everything in to an app bundle. Code distributed as a framework is generally pure Obj-C or they are already doing ABI testing for other reasons. It will force package authors to revisit this which may improve performance of their packages. I wouldn't lose any sleep over it. It could be a performance win for other Swift code and Swift 6 will require packages that want to take advantage of new features to update anyway, so not a big deal to do some minor refactoring. Since it is tied to the language mode, nobody will be forced to adopt it immediately.
+1 on this pitch overall. It seems worth it to me. It forces you to think about boxing/existentials which I think is good. There may be more type information which I think makes it a better default.
This is a very understandable concern but this proposal would not actually get rid of the clarity that comes with deciding between using some
and any
in your API. I would argue that deciding between some
and any
is a step beyond the decision to just add protocols to your code. Deciding to introduce a protocol into your code to allow for more abstraction should be considered a separate decision and this proposal would decouple those two decisions.
I see value in the suggestion to replace writing some
with writing bare protocols. But I do want to clarify something when you say "where bare protocol doesn't mean specifically some
"
If dropping some
syntax was a future direction we took, I think it would still make sense to have bare protocols mean some
implicitly because the current behavior of bare protocols meaning any
can still lead to needing to rewrite code if users run into a limitation with using existentials.
This proposal would re-introduce a third spelling for protocols which is valuable because it helps make it clear across language modes what you mean. Two spellings is a reasonable future direction but I think it should be phased in!
I appreciate the thoughtful feedback here!
Re: should P mean some P?
We've done some migration using this feature and so far our assumption is that most cases where a plain protocol is used, the user actually intended to use some
instead of the current default any
. I can elaborate more on this point in the proposal. I wouldn't say making bare protocols is really "making the language more opaque" though. Instead this proposal is improving the language design in such a way that enhances user experience; reduces the risk of running into issues that arise when advanced type erasure semantics are present unexpectedly and removing the load to think about such tradeoffs.
We are already encouraging to write some
by default because we've deemed any
is usually not what we want (which motivated SE-0335 ) For users who are new to generics, this extra step of writing some
by default seems less than desirable. Why require users who want to add abstraction to their code using protocols to deep dive into the semantic differences?
Re: Is Swift 6 the right time?
I understand the concern here but this is also very similar to the type of code that will break with implicit open existentials in Swift 6, SE-0352, so this proposal is in keeping with the direction we are heading in.
In the case where you leave your code as is, we are planning to include fixits to add any
for the function reference case and other edge cases we are surfacing through migration to help cut down on the need to manually track down such cases. But having an option to migrate all uses of P
to any P
is an interesting idea that I can consider. I can definitely add more depth to the proposal to discuss a migration plan and options!
My current opinion/gut feeling is against this pitch of allowing the elision of some
. I worry that blurring the line between types and type-constraints (protocols) is not a good thing, and that this pitch would rob us of the opportunity to escape from that issue in Swift 6.
I think that what could convince me that this is a good idea would be a thorough conceptual/philosophical breakdown of types vs protocols from someone with more well-rounded knowledge than I, in which the thesis is defended that preserving the distinction in the discussed syntax is more noise than signal.
If I truly believed in the change then getting to drop some
from a visual standpoint would be rather nice…
I think eliding some
will damage consistency with class
es in syntax.
class A {}
class B1: A {}
class B2: A {}
// possible
func foo() -> A {
if condition {
return B1()
} else {
return B2()
}
}
protocol P {}
struct S1: P {}
struct S2: P {}
// impossible
func foo() -> P {
if condition {
return S1()
} else {
return S2()
}
}