[Pitch] Require Any for existentials


(Anton Zhilin) #1

I haven't yet prepared a formal proposal, but the idea should be understood
by pretty much everyone. This topic has been discussed, but deferred to
"post-Swift3". I think the time for it has come.

Basically, the proposal is to make protocols non-types. To use protocol as
an existential type, it must be wrapped in Any:

protocol ExampleProtocol {
    func foo() -> Int
    func bar() -> String
}

func f<T: ExampleProtocol>() // OK

func f(x: ExampleProtocol) // error: ExampleProtocol is not a type
func f(x: Any<ExampleProtocol>) // OK

This will syntactically separate existential types from protocols
themselves and generic constraints.
The proposal does not allow for much variativity, and it looks like the
proposal can be easily created and submitted.


(Chris Lattner) #2

I haven't yet prepared a formal proposal, but the idea should be understood by pretty much everyone. This topic has been discussed, but deferred to "post-Swift3". I think the time for it has come.

Hi Anton,

This is a source breaking change, and we don’t have a framework established for how to handle those in the post-swift 3 world yet. The core team is focusing on finishing up Swift 3 right now, but I expect that one of the first topics will be to define and iterate on the model for handling source changes.

Only once that is established can we determine whether a proposal like this is possible and know what the tradeoffs are that it entails.

-Chris

···

On Aug 23, 2016, at 1:49 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Basically, the proposal is to make protocols non-types. To use protocol as an existential type, it must be wrapped in Any:

protocol ExampleProtocol {
    func foo() -> Int
    func bar() -> String
}

func f<T: ExampleProtocol>() // OK

func f(x: ExampleProtocol) // error: ExampleProtocol is not a type
func f(x: Any<ExampleProtocol>) // OK

This will syntactically separate existential types from protocols themselves and generic constraints.
The proposal does not allow for much variativity, and it looks like the proposal can be easily created and submitted.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #3

The basic design is fine, but I wouldn't want to add more noise to my code. We could keep (label: Protocol) as a shorthand form for (label: Any<Protocol>) similar to Optionals.

What's the benifite of this anywasys? What exactly will this solve?!

···

--
Adrian Zubarev
Sent with Airmail

Am 23. August 2016 um 23:18:35, Chris Lattner via swift-evolution (swift-evolution@swift.org(mailto:swift-evolution@swift.org)) schrieb:

On Aug 23, 2016, at 1:49 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
> I haven't yet prepared a formal proposal, but the idea should be understood by pretty much everyone. This topic has been discussed, but deferred to "post-Swift3". I think the time for it has come.

Hi Anton,

This is a source breaking change, and we don’t have a framework established for how to handle those in the post-swift 3 world yet. The core team is focusing on finishing up Swift 3 right now, but I expect that one of the first topics will be to define and iterate on the model for handling source changes.

Only once that is established can we determine whether a proposal like this is possible and know what the tradeoffs are that it entails.

-Chris

>
> Basically, the proposal is to make protocols non-types. To use protocol as an existential type, it must be wrapped in Any:
>
> protocol ExampleProtocol {
> func foo() -> Int
> func bar() -> String
> }
>
> func f<T: ExampleProtocol>() // OK
>
> func f(x: ExampleProtocol) // error: ExampleProtocol is not a type
> func f(x: Any<ExampleProtocol>) // OK
>
> This will syntactically separate existential types from protocols themselves and generic constraints.
> The proposal does not allow for much variativity, and it looks like the proposal can be easily created and submitted.
> _______________________________________________
> 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


(Haravikk) #4

Currently we can only use a protocol as if it were a type when it has no associated types, existentials will eliminate that restriction, allowing us to use a protocol Foo where we would currently either have to use AnyFoo or a very verbose generic constraint. I think the point of this proposal is to ask whether we should be able to use protocols as if they were concrete types, or whether it would be better to require Any<> to clarify what's really happening.

Personally I'm on the fence; I don't particularly mind being able to use a protocol name as if it were a concrete type, but I do generally prefer for things to be explicit wherever confusion may be caused. I think that requiring Any<> could be a good thing, as it clarifies that what you're interacting with is not a concrete type, and highlights that it may optimise differently (I'm not 100% on the details of how existentials actually work).

So I'd say I'm a tentative +0.5; in terms of migration I don't think this is all that source breaking, as it's very easy to add a fixit for (possibly even an automatic one?) and it only currently applies to non-generic protocols anyway, which seems relatively niche.

···

On 23 Aug 2016, at 23:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

The basic design is fine, but I wouldn't want to add more noise to my code. We could keep (label: Protocol) as a shorthand form for (label: Any<Protocol>) similar to Optionals.

What's the benifite of this anywasys? What exactly will this solve?!


(Karl) #5

The problem, as I understand it, is that when you use “MyProtocol” as a type, there is sometimes a confusion as to whether you mean:
- the protocol itself, or
- anything which “is” that protocol (similar to how I might write “MyClass” when meaning anything which “is” a MyClass, including MyDerviedClass), i.e. an existential

See the discussion about protocol self-conformance. Any<MyProtocol> would make it clear that you mean the latter.

I quite like using protocol names to refer to existentials. I’d like to consider the other options for disambiguating the protocol type before giving a +/- 1. This would fit nicely with another commonly-requested feature though: the ability to constrain concrete types by protocol conformance - e.g. UIView<MyProtocol>.

Karl

···

On 24 Aug 2016, at 00:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

The basic design is fine, but I wouldn't want to add more noise to my code. We could keep (label: Protocol) as a shorthand form for (label: Any<Protocol>) similar to Optionals.

What's the benifite of this anywasys? What exactly will this solve?!

--
Adrian Zubarev
Sent with Airmail


(Vladimir) #6

The basic design is fine, but I wouldn't want to add more noise to my
code. We could keep (label: Protocol) as a shorthand form for (label:
Any<Protocol>) similar to Optionals.

What's the benifite of this anywasys? What exactly will this solve?!

Currently we can only use a protocol as if it were a type when it has no
associated types, existentials will eliminate that restriction, allowing
us to use a protocol Foo where we would currently either have to use
AnyFoo or a very verbose generic constraint. I think the point of this
proposal is to ask whether we should be able to use protocols as if they
were concrete types, or whether it would be better to require Any<> to
clarify what's really happening.

I believe Any<> could remove some inconsistency/confusion when protocol is used as generic constraint in 'where' clause. Let's see:

we can have such function:
func process<T: Sequence>(_ seq: T) where T.Iterator.Element == MyElement
and we can have such :
func process<T: Sequence>(_ seq: T) where T.Iterator.Element : MyElement

the first will accept array only if typed as [MyElement] i.e.
let arr : [MyElement] = ...
, it is logical as constraint defined with '==' i.e. type is strictly equal.

but the second form will not allow you to use instance typed as [MyElement] or as any derived protocol like [ProtocolDerivedFromMyElement] - although one can expect this in analogue with class/value type in constraint - but will accept array *typed* as class/struct conformed to MyElement protocol, i.e.
struct S: MyElement {..}
let arr : [S] = ...

In case we'll have Any<>, the second definition as I understand should looks like:

func process<T: Sequence>(_ seq: T) where T.Iterator.Element : Any<MyElement>

i.e. in this case it clearly show that it will accept array(sequence) typed as some concrete type conformed to protocol.

···

On 24.08.2016 13:23, Haravikk via swift-evolution wrote:

On 23 Aug 2016, at 23:14, Adrian Zubarev via swift-evolution >> <swift-evolution@swift.org> wrote:

Personally I'm on the fence; I don't particularly mind being able to use
a protocol name as if it were a concrete type, but I do generally prefer
for things to be explicit wherever confusion may be caused. I think that
requiring Any<> could be a good thing, as it clarifies that what you're
interacting with is not a concrete type, and highlights that it may
optimise differently (I'm not 100% on the details of how existentials
actually work).

So I'd say I'm a tentative +0.5; in terms of migration I don't think
this is all that source breaking, as it's very easy to add a fixit for
(possibly even an automatic one?) and it only currently applies to
non-generic protocols anyway, which seems relatively niche.
_______________________________________________ swift-evolution mailing
list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Anton Zhilin) #7

Syntax for `T == P || T: P` is off-topic for current proposal, which is
just about replacing `P` with `Any<P>`.

Currently, equivalent of `Any<P>` has no subtypes (besides itself). This is
likely not going to be changed. So `where U : Any<P>` will not be allowed.

Generalized existentials also don't solve this problem. Existential
`Sequence` would look like this:

`Any<Sequence where Iterator.Element : P>`

Here, we can't accept `Iterator.Element == Any<P>` for the same reason that
we can't accept `Any<P> : P` in any other place: static member requirements
are not implemented.

To solve this problem, we should invent another generic requirement, say
`:==`, that prohibits calling static members on generic parameter. This
should be a separate proposal. `Any<P>` can't help here, because the
problem with calling static members only happens with static polymorphism.