[Pitch] Elide `some` in Swift 6

Eh? I'm not sure anybody is suggesting taking any rocket trips because it will be fast. I'm merely trying to point out there are alternatives to "extinction event" level source breaking with Swift 6. I wheeled in the benchmarking to support that if we kept our feet on the ground retaining some eliding to any there wouldn't be an appreciable cost as the discussion had drifted back to hyperbole about performance.

i don’t know if i’m the only one, but i personally am having a hard time following this discussion. are the two of you referring to source compatibility of elided some as the “it would be fast” concept, or are you referring to some performance gain that would be had by eliding some?

1 Like

Maybe the argument is that allowing the elision of some in Swift 6 will cause most of the current bare protocol usages to silently become more performant

Yeah, it's got a little muddled as the original pitch was to elide to some which I don't think is viable and I started up a counter-pitch to retain some eliding to any while eliding to some for procedure arguments only though few have picked up on that. I do think it's worth continuing to talk about in the meantime as the next Swift version is tagged as 5.9 so we should have a little more time to evaluate alternatives.

1 Like
An anecdote from yesterday against allowing the elision of `some` ...

Yesterday I was speaking to a junior developer whose task was to make a new view controller with a certain set of simple characteristics, and he was attempting to copy and adapt the patterns he saw being employed in other view controllers. He ended up feeling the need to ask me for help, which after hearing him out I discovered was essentially entirely due to his confusion about what protocols really are and how they work.

One punch line of the situation was that his code already was correct by the time he reached out to me - he just had some wires crossed with regard to protocols which made it such that despite the fact that he had correctly copied and modified the other code, he was certain that it must not be setup correctly (because the role of the protocol mystified him).

I believe that the constant presence of one of the two keywords (some or any) very well could have caused his particular confusion about protocols to evaporate much earlier on his path as a developer, not only preventing him from feeling stuck in the situation that he was in this time, but also allowing him to write better code in general due to a better grasp of the tools he has at hand when writing Swift.

Basically, the view controller that he was using as a template had an initializer like this:

init (viewModel: PreexistingViewControllerViewModelProtocol) {
    ...
}

One line that struck me was that he said, "I mean if it were a struct, I'd know how that works."

I believe his confusion came about more or less like this:

  1. He had an understanding of what it means to define his own structs or classes and create instances of them.
  2. He had an understanding about what it means to invoke a function with an instance of a struct or a class as an argument.
  3. He had been forced to interact with protocols on various occasions but had never really finished absorbing the essence of what they are/how they work/when to use them.
  4. He had the notion that protocols "are, like, empty, right? They don't have real implementations or something?"
  5. He saw an initializer that looked exactly like initializers he had worked with dozens of times in the past, with the only exception being that when he jumped to the definition of the argument type he found a protocol, rather than a struct or a class like he was used to.
  6. To him, that seemed like a fatal substitution (struct -> protocol), because protocols are "empty" - he felt that he hadn't yet understood the types of abstract, high-brow situations in which it is somehow useful to work with "empty" versions of the useful things that he was used to working with, but it certainly didn't seem likely to him that an "empty" view model was going to suffice to make his actual view controller work.

I think that his confusion that I just witnessed "in the wild" is exactly the confusion that one would be worried would arise from allowing the elision of some. I think some ViewModelProtocol or any ViewModelProtocol would contribute greatly to dispelling the confusing illusion that we are ever manipulating values that are somehow "empty".

23 Likes

Any and AnyObject protocols seem to be overlooked.

The expression some Any is currently used to express the unconstrained arbitrary generic parameter T: Any. And as an exception for SE-0335, any can be (and should be?) elided in any Any and Any is possible.
Therefore, func foo(value: Any) is still valid in Swift 6 and then reanalyzed as func foo(value: some Any) when we accept eliding some, that will cause a source break.

One possible solution for this is, obviously, any Any… but I hate this.
Another solution is making Any exception for some elision. Is it acceptable?

1 Like

I did point out this issue a long time ago in this thread, but I’m glad not to be the only one who is concerned.

1 Like

Strictly speaking, Any and AnyObject are not protocols. It’s unclear whether this pitch actually requires them to be prefixed with some.

1 Like

They are protocols in the way they’re presented in the surface language. (We just had this discussion.)

I recall that discussion happening, which is where the I got the idea that they’re not really protocols. :blush:

It’s hard to search for Any and AnyObject on the forums. Do you have a link handy to that discussion? I seem to recall it was in the context of some other constraint (copyability?).

1 Like

Which I suppose isn’t technically accurate in shipping Swift, but it seems reasonable aligning the type system to the conceptual semantic as a future direction.

Is another possible solution to define a new entity (type-constraint/protocol) named Unconstrained (or something similar) and then use it to redefine Any like this?

typealias Any = any Unconstrained

AnyObject isn't a protocol, it's a layout constraint. you can't retroactively add an AnyObject conformance, because there is no such thing as "conforming" to AnyObject. things can be AnyObject, but they cannot implement AnyObject.

this isn't a compiler implemention detail, it is a very public, surfaced concept in the language.

i really wish we would stop calling this thing a "protocol", because it isn't.

(by the way, we don't "surface" it as a protocol even at the documentation level. according to SymbolGraphGen, AnyObject is a typealias with no definition.)

3 Likes

Let’s not confuse “is” and “ought.”

AnyObject is explicitly documented as a protocol—this is just a fact. That it is declared as a type alias to a built-in is immaterial; you wouldn’t go around telling people they’re wrong about Unicode.Encoding being a type because it’s a type alias of an underscored type. Nor have we ever said that the ability to retroactively conform is a tentpole characteristic of a protocol (and, incidentally, our diagnostic text does specifically call it conforming to AnyObject). In the surface language, there are concrete types and there are protocols, and AnyObject pointedly is not a superclass.

(Certainly, if we accumulate a critical mass of layout constraints and one can demonstrate actual confusion that arises among users due to their being presented as protocols, it can be a point of review in a future proposal to review the concept of a layout constraint as distinct from protocols.)

Not that it makes a lick of difference for the purposes of this proposal to argue that a layout constraint shouldn’t be a kind of protocol—you’d just end up having to find-and-replace “protocol” with “protocol or layout constraint.” …Unless you want to make the argument that the same shorthand syntax for a generic parameter should mean something different for layout contraints than for other protocols, which I’d be interested to hear.

1 Like

alas, saying something over and over again does not make it true. the documentation is misleading and should be rewritten, and the diagnostic text should be improved.

typealias Unicode.Encoding = _UnicodeEncoding is not an applicable analogy, because _UnicodeEncoding is a protocol. in essence, Unicode.Encoding is a “real” typealias, which has an RHS that points to a protocol, so it is correct in a sense to say that Unicode.Encoding “is” a protocol, in the same way that Void “is” the empty tuple ().

AnyObject is not a “real typealias”, it has no RHS, and it cannot possibly have an RHS because it doesn’t point to a protocol, or really, any type at all. we call it a “typealias” because we don’t have a better keyword to prefix it with.

what exactly are the tentpole characteristics of a protocol then?

2 Likes

That’s a fiction that the documentation employs to avoid talking about layout constraints, none of which other than Any and AnyObject are currently public.

I don’t think that follows. Surely neither this proposal nor any other has contemplated some _Trivial?

2 Likes

TSPL seems pretty unambiguous about it:

Currently the documentation contradicts itself by claiming that AnyObject is a protocol while also saying that you can do things with protocols that you can't do with AnyObject. I always assumed that the intention was that while it's not a protocol it's pretty close to one and there aren't enough things that are whatever it is to justify a new term. If it is supposed to be just a protocol then I'm left fairly confused about what exactly a protocol is.

5 Likes

Exactly that is surely contemplated:


You can indeed extend an existing type to adopt and conform to a new protocol. The built-in AnyObject is, by definition, never new. TSPL very deftly doesn't describe conforming a type you don't own to a protocol you don't own, which—as noted in SE-0364—can lead to unpredictable behavior and will soon be called out as unsupported with a warning.

You're mistaken: there really is in fact a typealias declaration with an actual, honest-to-goodness, right-hand side; it's just that it must be censored by SymbolGraphGen because it refers to a built-in:

public typealias AnyObject = Builtin.AnyObject

I wouldn't call it a fiction; it's the user-facing language model, which glosses over implementation details that we deliberately don't want to make the concern of the end user.

It also isn't something invented by the documentation. Until 2017, AnyObject was literally declared as a protocol:

#if _runtime(_ObjC)
@objc
public protocol AnyObject : class {}
#else
public protocol AnyObject : class {}
#endif

(And even when it was declared as such, there were special-cased checks in the compiler to stop you from extending it with new methods, or declaring a retroactive conformance to it, etc.)

A very involved series of PRs revised the underlying implementation such that the declaration assumed its present form as a type alias (if I recall correctly, this was around the time when we decided to deprecate the class syntax in favor of AnyObject).


On both this point and the preceding one, I've already replied at length in the relevant thread, so I'll just point you there instead of further derailing this one:

Let's focus here on only any salient points that would relate to eliding some.

3 Likes