PITCH: New :== operator for generic constraints

Any proposal that expands the power of generic programming gets an almost
automatic +1 from me.

I can't think of any circumstances in which I wouldn't want to use ":=="
instead of ":". Are there any downsides to expanding ":" to mean what ":=="
does?

Incidentally, I kinda thought things either already worked like this, or
would work like this after generics were "completed", but I can't tell you
why I thought that.

Me neither, but the last time I proposed that, people stated that there
were some cases where this could not work. No concrete examples were given,
but I assume it probably has something to do with associated type
wackiness. :== seems like a workable compromise to me.

If an existential of a protocol P doesn't conform to itself, what can you
do inside the body of a generic function that has a generic constraint
specified with `:==`? In other words, what would we know about what's in
common between such an existential of a protocol and types that conform to
the protocol?

My proposal is that in such cases, using :== would lead to a compiler
error.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the point is that existentials not conforming to their protocols
is the underlying reason this doesn’t work already.

From the previous thread:

There are a couple of reasons this is the case. IIRC in some cases it actually isn't possible for the existential to conform to the protocol in a sound way. And even when it is possible, I believe it has been said that it is more difficult to implement than you might think. Hopefully the situation will improve in the future but I'm not aware of any specific plans at the moment.

It seems like a reasonably straightforward axiom. I would be interested to
learn more about those edge-cases.

Hmm, re-reading that makes me worry about this proposal at a practical
level. IIUC, this is something that is pretty much desired across the
board, and if we could have it for `:` it'd be best.
But, it sounds like the reason `:` doesn't work that way isn't that the
core team has a different opinion, but rather that it's very difficult to
implement. And, IIUC, the situation is that no protocol existential
currently conforms to itself, not that some do and some don't. The
implementation work wouldn't be any easier if we called the operator
`:==`...

···

On Tue, Aug 16, 2016 at 6:59 PM, Karl <razielim@gmail.com> wrote:

On 17 Aug 2016, at 00:34, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:
On Aug 16, 2016, at 5:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 16, 2016 at 5:19 PM, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:

On Aug 16, 2016, at 5:13 PM, David Sweeris <davesweeris@mac.com> wrote:

Karl

Now that you mention it, I very vaguely recall that thread. What it called again? I want to go back and read that whole section.

- Dave Sweeris

···

On Aug 16, 2016, at 6:59 PM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

I think the point is that existentials not conforming to their protocols is the underlying reason this doesn’t work already.

From the previous thread:

There are a couple of reasons this is the case. IIRC in some cases it actually isn't possible for the existential to conform to the protocol in a sound way. And even when it is possible, I believe it has been said that it is more difficult to implement than you might think. Hopefully the situation will improve in the future but I'm not aware of any specific plans at the moment.

It seems like a reasonably straightforward axiom. I would be interested to learn more about those edge-cases.

There are various ways to work around the problem using a new operator, though, depending on how much work you wanted to do in implementing it. At the very least you could take the ‘preprocessor’ approach and turn one function using :== into two separate functions, one using == and one using :, with both functions sharing the same body. This would still have the code bloat in the binary, but at least it wouldn’t be bloating up the source. We could then solve some of the binary bloat by spinning off the function body into a third function and having both the two original functions call that. Or maybe we could have the : variant reinterpret the sequence as an == sequence and send it to the == variant. There are multiple ways that this could be handled without forcing protocols to conform to themselves.

Charles

···

On Aug 16, 2016, at 7:08 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Aug 16, 2016 at 6:59 PM, Karl <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 17 Aug 2016, at 00:34, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 16, 2016, at 5:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Tue, Aug 16, 2016 at 5:19 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 16, 2016, at 5:13 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

Any proposal that expands the power of generic programming gets an almost automatic +1 from me.

I can't think of any circumstances in which I wouldn't want to use ":==" instead of ":". Are there any downsides to expanding ":" to mean what ":==" does?

Incidentally, I kinda thought things either already worked like this, or would work like this after generics were "completed", but I can't tell you why I thought that.

Me neither, but the last time I proposed that, people stated that there were some cases where this could not work. No concrete examples were given, but I assume it probably has something to do with associated type wackiness. :== seems like a workable compromise to me.

If an existential of a protocol P doesn't conform to itself, what can you do inside the body of a generic function that has a generic constraint specified with `:==`? In other words, what would we know about what's in common between such an existential of a protocol and types that conform to the protocol?

My proposal is that in such cases, using :== would lead to a compiler error.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the point is that existentials not conforming to their protocols is the underlying reason this doesn’t work already.

From the previous thread:

There are a couple of reasons this is the case. IIRC in some cases it actually isn't possible for the existential to conform to the protocol in a sound way. And even when it is possible, I believe it has been said that it is more difficult to implement than you might think. Hopefully the situation will improve in the future but I'm not aware of any specific plans at the moment.

It seems like a reasonably straightforward axiom. I would be interested to learn more about those edge-cases.

Hmm, re-reading that makes me worry about this proposal at a practical level. IIUC, this is something that is pretty much desired across the board, and if we could have it for `:` it'd be best.
But, it sounds like the reason `:` doesn't work that way isn't that the core team has a different opinion, but rather that it's very difficult to implement. And, IIUC, the situation is that no protocol existential currently conforms to itself, not that some do and some don't. The implementation work wouldn't be any easier if we called the operator `:==`...

Any proposal that expands the power of generic programming gets an
almost automatic +1 from me.

I can't think of any circumstances in which I wouldn't want to use ":=="
instead of ":". Are there any downsides to expanding ":" to mean what ":=="
does?

Incidentally, I kinda thought things either already worked like this, or
would work like this after generics were "completed", but I can't tell you
why I thought that.

Me neither, but the last time I proposed that, people stated that there
were some cases where this could not work. No concrete examples were given,
but I assume it probably has something to do with associated type
wackiness. :== seems like a workable compromise to me.

If an existential of a protocol P doesn't conform to itself, what can you
do inside the body of a generic function that has a generic constraint
specified with `:==`? In other words, what would we know about what's in
common between such an existential of a protocol and types that conform to
the protocol?

My proposal is that in such cases, using :== would lead to a compiler
error.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the point is that existentials not conforming to their protocols
is the underlying reason this doesn’t work already.

From the previous thread:

There are a couple of reasons this is the case. IIRC in some cases it actually isn't possible for the existential to conform to the protocol in a sound way. And even when it is possible, I believe it has been said that it is more difficult to implement than you might think. Hopefully the situation will improve in the future but I'm not aware of any specific plans at the moment.

It seems like a reasonably straightforward axiom. I would be interested
to learn more about those edge-cases.

Hmm, re-reading that makes me worry about this proposal at a practical
level. IIUC, this is something that is pretty much desired across the
board, and if we could have it for `:` it'd be best.
But, it sounds like the reason `:` doesn't work that way isn't that the
core team has a different opinion, but rather that it's very difficult to
implement. And, IIUC, the situation is that no protocol existential
currently conforms to itself, not that some do and some don't. The
implementation work wouldn't be any easier if we called the operator
`:==`...

There are various ways to work around the problem using a new operator,
though, depending on how much work you wanted to do in implementing it. At
the very least you could take the ‘preprocessor’ approach and turn one
function using :== into two separate functions, one using == and one using
:, with both functions sharing the same body. This would still have the
code bloat in the binary, but at least it wouldn’t be bloating up the
source. We could then solve some of the binary bloat by spinning off the
function body into a third function and having both the two original
functions call that. Or maybe we could have the : variant reinterpret the
sequence as an == sequence and send it to the == variant. There are
multiple ways that this could be handled without forcing protocols to
conform to themselves.

Not an expert, but wouldn't this blow up a whole bunch of compile-time
"stuff" like the type checker? If no protocol existential conforms to
itself, and it's unknown to the compiler whether it's even theoretically
possible for a particular protocol, then what's to say what the body of a
function that uses this constraint should be able to do?

···

On Tue, Aug 16, 2016 at 7:43 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 16, 2016, at 7:08 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 16, 2016 at 6:59 PM, Karl <razielim@gmail.com> wrote:

On 17 Aug 2016, at 00:34, Charles Srstka via swift-evolution < >> swift-evolution@swift.org> wrote:
On Aug 16, 2016, at 5:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Aug 16, 2016 at 5:19 PM, Charles Srstka via swift-evolution < >> swift-evolution@swift.org> wrote:

On Aug 16, 2016, at 5:13 PM, David Sweeris <davesweeris@mac.com> wrote:

Charles

What happens if you try to do that by hand?

func foo<Foo: Sequence>(bar: Foo) where Foo.Iterator.Element == MyProto {
    for eachFoo in bar {
        eachFoo.baz()
    }
}

func foo<Foo: Sequence>(bar: Foo) where Foo.Iterator.Element: MyProto {
    for eachFoo in bar {
        eachFoo.baz()
    }
}

Answer: If either Element == MyProto or Element: MyProto can’t handle the baz() method, it’ll throw a compiler error.

Generating the above code via a preprocessor would get the same result. :== would only compile if the body worked with both == and with :.

Charles

···

On Aug 16, 2016, at 7:48 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Aug 16, 2016 at 7:43 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 16, 2016, at 7:08 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Tue, Aug 16, 2016 at 6:59 PM, Karl <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 17 Aug 2016, at 00:34, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 16, 2016, at 5:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Tue, Aug 16, 2016 at 5:19 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 16, 2016, at 5:13 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

Any proposal that expands the power of generic programming gets an almost automatic +1 from me.

I can't think of any circumstances in which I wouldn't want to use ":==" instead of ":". Are there any downsides to expanding ":" to mean what ":==" does?

Incidentally, I kinda thought things either already worked like this, or would work like this after generics were "completed", but I can't tell you why I thought that.

Me neither, but the last time I proposed that, people stated that there were some cases where this could not work. No concrete examples were given, but I assume it probably has something to do with associated type wackiness. :== seems like a workable compromise to me.

If an existential of a protocol P doesn't conform to itself, what can you do inside the body of a generic function that has a generic constraint specified with `:==`? In other words, what would we know about what's in common between such an existential of a protocol and types that conform to the protocol?

My proposal is that in such cases, using :== would lead to a compiler error.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the point is that existentials not conforming to their protocols is the underlying reason this doesn’t work already.

From the previous thread:

There are a couple of reasons this is the case. IIRC in some cases it actually isn't possible for the existential to conform to the protocol in a sound way. And even when it is possible, I believe it has been said that it is more difficult to implement than you might think. Hopefully the situation will improve in the future but I'm not aware of any specific plans at the moment.

It seems like a reasonably straightforward axiom. I would be interested to learn more about those edge-cases.

Hmm, re-reading that makes me worry about this proposal at a practical level. IIUC, this is something that is pretty much desired across the board, and if we could have it for `:` it'd be best.
But, it sounds like the reason `:` doesn't work that way isn't that the core team has a different opinion, but rather that it's very difficult to implement. And, IIUC, the situation is that no protocol existential currently conforms to itself, not that some do and some don't. The implementation work wouldn't be any easier if we called the operator `:==`...

There are various ways to work around the problem using a new operator, though, depending on how much work you wanted to do in implementing it. At the very least you could take the ‘preprocessor’ approach and turn one function using :== into two separate functions, one using == and one using :, with both functions sharing the same body. This would still have the code bloat in the binary, but at least it wouldn’t be bloating up the source. We could then solve some of the binary bloat by spinning off the function body into a third function and having both the two original functions call that. Or maybe we could have the : variant reinterpret the sequence as an == sequence and send it to the == variant. There are multiple ways that this could be handled without forcing protocols to conform to themselves.

Not an expert, but wouldn't this blow up a whole bunch of compile-time "stuff" like the type checker? If no protocol existential conforms to itself, and it's unknown to the compiler whether it's even theoretically possible for a particular protocol, then what's to say what the body of a function that uses this constraint should be able to do?

The problem is that:
protocol should not be a type, but it is a type sometime and not type
sometime now.

for exam:
P.Type not same as T.Type

But you can declare a variable of type P.

Protocol should be a contract only, no instances of it.

Charles Srstka via swift-evolution <swift-evolution@swift.org>于2016年8月17日
周三14:11写道:

···

On Aug 17, 2016, at 12:35 AM, Slava Pestov <spestov@apple.com> wrote:

On Aug 16, 2016, at 10:16 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 16, 2016, at 11:42 PM, Slava Pestov <spestov@apple.com> wrote:

Argh, that’s particularly frustrating since in something like ‘func foo<T
: P>(t: T)’ or ‘func foo<S : Sequence>(s: S) where S.IteratorElement: P’,
you’re only ever getting instances anyway since the parameter is in the
input, so calling initializers or static functions isn’t something you can
even do (unless you call .dynamicType, at which point you *do* have a
concrete type at runtime thanks to the dynamic check).

Well, if you have ‘func foo<T : P>(t: T)’, then you can write
T.someStaticMember() to call static members — it’s true you also have an
instance ’t’, but you can also work directly with the type. But I suspect
this is not what you meant, because:

Agh, you’re right, I’d forgotten about that. It’s days like this that I
miss Objective-C’s “It just works” dynamism. ;-)

Objective-C doesn’t have an equivalent of associated types or
contravariant Self, but I understand your frustration, because Sequence and
Equatable are pervasive in Swift.

I was thinking of Equatable, which in Objective-C was just the -isEqual:
method on NSObject, which we usually just started with a dynamic type check
in the cases where that mattered. I’m sure performance on Swift’s version
is much better, but the ObjC way was refreshingly surprise-free.

The other trouble is that it’s not just confusing; it can very easily get
in the way of your work even if you know exactly what’s going on,
necessitating kludges like AnyHashable just to do things like have a
dictionary that can take more than one key type (an example that’s
particularly irritating since the only method you care about, hashValue, is
just a plain old Int that doesn’t care about the Self requirement at all).
I know that a while ago I ended up using my own Equatable substitute with
an ObjC-style isEqual() method on some types, just because actually
implementing Equatable was throwing a huge spanner into the rest of the
design.

Yeah, AnyHashable is basically a hand-coded existential type. It would
also be possible to do something similar for Equatable, where an
AnyEquatable type could return false for two values with differing concrete
types, removing the need for an == with contra-variant Self parameters.

Also: changing something into a class when it otherwise didn’t need to be
one, so you can use an ObjectIdentifier as a dictionary key, because using
a protocol that conformed to Hashable was dropping an atom bomb on the
entire rest of the project.

Generalized existentials eliminate the restriction and thus the hacks. On
the other hand, they add yet more complexity to the language, so designing
them correctly involves difficult tradeoffs.

Fair enough. I guess I’ll wait it out a bit and see what the team comes up
with.

Well, the idea was to create an easier-to-implement alternative to
self-conforming protocols, which could be done if :== were expanded to one
function that uses ==, and another with the same body that uses :, because
I was under the impression that the compiler team did not want to implement
self-conforming protocols.

I think the underlying machinery would be the same. We only want to
compile the body of a generic function body, without any kind of cloning
like in C++ templates, producing a general uninstantiated runtime form. So
:== T requirements would effectively require self-conforming protocols
anyway, since your function will have to dynamically handle both cases.

The implementation for self-conforming opaque protocols is not difficult,
because the value itself can already be of any size, so it’s really not a
problem to have an existential in there. In theory, someone could cook it
up in a week or so.

For class protocols, I don’t know how to do it without an efficiency hit
unfortunately.

Consider these two functions, taking a homogeneous and heterogeneous array
of a class-bound protocol type:

protocol P : class {}

func f<T : P>(array: [T]) {} // this takes an array of pointers to T,
because there’s only one witness table for all of them
func ff(array: [P]) {} // this takes an array of <T, witness table> pairs,
two pointers each, because each element can be a different concrete type

What you’re saying is that f() should in fact allow both representations,
because you’ll be able to call f() with a value of type [P]. Right now, if
we know a generic parameter is class-constrained, we use a much more
efficient representation for values of that type, that is known to be fixed
size in the LLVM IR. We would have to give that up to allow
class-constrained existentials to self-conform, since now a
class-constrained parameter can be an existential with any number of
witness tables.

There might be some trick for doing this efficiently, but I don’t know of
one yet.

Of course, we can just say that class-constrained protocols never
self-conform, unless they’re @objc. That seems like a hell of an esoteric
restriction though (can you imagine trying to come up with a clear phrasing
for *that* diagnostic?)

And if you’re wondering, the reason that @objc protocols self-conform in
Swift today, is because they their existentials don’t have *any* witness
tables — @objc protocol method bodies are found by looking inside the
instance itself.

AnyObject is the other kind of protocol that self-conforms — you can use
it both as a generic constraint, and as a concrete type bound to a generic
parameter, and it ‘just works’, because again it doesn’t have a witness
table.

Ah… because of the static dispatch, mapping the protocol members to
address offsets which may vary from member to member, as opposed to @objc
protocols, which I’d guess are probably doing the old-school lookup by
selector name à la objc_msgSend(). Hmm. I’d still probably argue that it’s
worth it, because I get the impression that Apple prefers the use of
generic sequence and collections for parameters rather than hard-coding
arrays, and frankly, with the current behavior it is slightly difficult to
do that. I guess it’s up to the compiler team, though.

I will say that this has been an interesting discussion. Thanks for
offering your knowledge and insight.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

The problem is that:
protocol should not be a type, but it is a type sometime and not type
sometime now.

for exam:
P.Type not same as T.Type

But you can declare a variable of type P.

Protocol should be a contract only, no instances of it.

I'm also confused about this. Tried to follow the whole discussion of experienced(as I understand) developer Charles and a member of core team, and saw that even experienced developer had some troubles to understand all the catches of protocols in Swift.

How we want to make less experienced developers to understand Swift's model of protocols&conformance&generic&existentials&etc.. While problems begins with the most used Equatable/Hashable protocols..

Also I believe the syntax of generic constraints in 'where' part is confusing. I'm about this:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element == Pepper

If '== Proto' means *exactly* typed as such protocol("let arr : [Proto] = .."), so IMO ': Proto' should means *typed* as exactly this protocol or any derived protocol("let arr : [ProtocolDerivedFromProto] = .."). Just in symmetry with class types in 'where' clause here.

Currently, how to specify 'protocol Proto or its derived protocols" ?

I suggest to discuss changing the rules for 'where' generic filter for protocols:

1. Don't allow syntax ': P' or '== P' for protocols. Such syntax should be allowed only for classes, structs & other value types.

2. For protocol introduce 'is P' syntax, which should mean 'typed exactly as P, or its derived protocol, or as concrete type conformed to P'. I.e.:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element is Pepper

3. If it is hard to implement (2) today in the "right" way, implement it with two copies of the same function, one with ":P" and one with "==P"(was discussed earlier in thread)

And, yes, IMO it is clear that we should be able to specify 'protocol Proto, its derived protocol, or any concrete type conformed to Proto' just because we are defining function that work with items that conforms to some contract, we are not interested how it is *defined* later in code, but *what it is* actually.
And because we *can* implement this now by copy/paste the *same function* with *same* name with 1 symbol changed - so, after all, I believe compiler can simulate this for us.

But yes, we all will wait what core team suggest as improvement in this area.

···

On 17.08.2016 13:00, Boris Wang via swift-evolution wrote:

Charles Srstka via swift-evolution <swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>于2016年8月17日 周三14:11写道:

    On Aug 17, 2016, at 12:35 AM, Slava Pestov <spestov@apple.com >> <mailto:spestov@apple.com>> wrote:

    On Aug 16, 2016, at 10:16 PM, Charles Srstka >>> <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

    On Aug 16, 2016, at 11:42 PM, Slava Pestov <spestov@apple.com >>>> <mailto:spestov@apple.com>> wrote:

    Argh, that’s particularly frustrating since in something like
    ‘func foo<T : P>(t: T)’ or ‘func foo<S : Sequence>(s: S) where
    S.IteratorElement: P’, you’re only ever getting instances anyway
    since the parameter is in the input, so calling initializers or
    static functions isn’t something you can even do (unless you call
    .dynamicType, at which point you *do* have a concrete type at
    runtime thanks to the dynamic check).

    Well, if you have ‘func foo<T : P>(t: T)’, then you can write
    T.someStaticMember() to call static members — it’s true you also
    have an instance ’t’, but you can also work directly with the type.
    But I suspect this is not what you meant, because:

    Agh, you’re right, I’d forgotten about that. It’s days like this
    that I miss Objective-C’s “It just works” dynamism. ;-)

    Objective-C doesn’t have an equivalent of associated types or
    contravariant Self, but I understand your frustration, because
    Sequence and Equatable are pervasive in Swift.

    I was thinking of Equatable, which in Objective-C was just the
    -isEqual: method on NSObject, which we usually just started with a
    dynamic type check in the cases where that mattered. I’m sure
    performance on Swift’s version is much better, but the ObjC way was
    refreshingly surprise-free.

    The other trouble is that it’s not just confusing; it can very
    easily get in the way of your work even if you know exactly what’s
    going on, necessitating kludges like AnyHashable just to do things
    like have a dictionary that can take more than one key type (an
    example that’s particularly irritating since the only method you
    care about, hashValue, is just a plain old Int that doesn’t care
    about the Self requirement at all). I know that a while ago I ended
    up using my own Equatable substitute with an ObjC-style isEqual()
    method on some types, just because actually implementing Equatable
    was throwing a huge spanner into the rest of the design.

    Yeah, AnyHashable is basically a hand-coded existential type. It
    would also be possible to do something similar for Equatable, where
    an AnyEquatable type could return false for two values with differing
    concrete types, removing the need for an == with contra-variant Self
    parameters.

    Also: changing something into a class when it otherwise didn’t need to
    be one, so you can use an ObjectIdentifier as a dictionary key, because
    using a protocol that conformed to Hashable was dropping an atom bomb
    on the entire rest of the project.

    Generalized existentials eliminate the restriction and thus the
    hacks. On the other hand, they add yet more complexity to the
    language, so designing them correctly involves difficult tradeoffs.

    Fair enough. I guess I’ll wait it out a bit and see what the team comes
    up with.

    Well, the idea was to create an easier-to-implement alternative to
    self-conforming protocols, which could be done if :== were expanded
    to one function that uses ==, and another with the same body that
    uses :, because I was under the impression that the compiler team
    did not want to implement self-conforming protocols.

    I think the underlying machinery would be the same. We only want to
    compile the body of a generic function body, without any kind of
    cloning like in C++ templates, producing a general uninstantiated
    runtime form. So :== T requirements would effectively require
    self-conforming protocols anyway, since your function will have to
    dynamically handle both cases.

    The implementation for self-conforming opaque protocols is not
    difficult, because the value itself can already be of any size, so
    it’s really not a problem to have an existential in there. In theory,
    someone could cook it up in a week or so.

    For class protocols, I don’t know how to do it without an efficiency
    hit unfortunately.

    Consider these two functions, taking a homogeneous and heterogeneous
    array of a class-bound protocol type:

    protocol P : class {}

    func f<T : P>(array: [T]) {} // this takes an array of pointers to T,
    because there’s only one witness table for all of them
    func ff(array: [P]) {} // this takes an array of <T, witness table>
    pairs, two pointers each, because each element can be a different
    concrete type

    What you’re saying is that f() should in fact allow both
    representations, because you’ll be able to call f() with a value of
    type [P]. Right now, if we know a generic parameter is
    class-constrained, we use a much more efficient representation for
    values of that type, that is known to be fixed size in the LLVM IR.
    We would have to give that up to allow class-constrained existentials
    to self-conform, since now a class-constrained parameter can be an
    existential with any number of witness tables.

    There might be some trick for doing this efficiently, but I don’t
    know of one yet.

    Of course, we can just say that class-constrained protocols never
    self-conform, unless they’re @objc. That seems like a hell of an
    esoteric restriction though (can you imagine trying to come up with a
    clear phrasing for *that* diagnostic?)

    And if you’re wondering, the reason that @objc protocols self-conform
    in Swift today, is because they their existentials don’t have *any*
    witness tables — @objc protocol method bodies are found by looking
    inside the instance itself.

    AnyObject is the other kind of protocol that self-conforms — you can
    use it both as a generic constraint, and as a concrete type bound to
    a generic parameter, and it ‘just works’, because again it doesn’t
    have a witness table.

    Ah… because of the static dispatch, mapping the protocol members to
    address offsets which may vary from member to member, as opposed to
    @objc protocols, which I’d guess are probably doing the old-school
    lookup by selector name à la objc_msgSend(). Hmm. I’d still probably
    argue that it’s worth it, because I get the impression that Apple
    prefers the use of generic sequence and collections for parameters
    rather than hard-coding arrays, and frankly, with the current behavior
    it is slightly difficult to do that. I guess it’s up to the compiler
    team, though.

    I will say that this has been an interesting discussion. Thanks for
    offering your knowledge and insight.

    Charles

    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org <mailto: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

The problem is that:
protocol should not be a type, but it is a type sometime and not type
sometime now.

for exam:
P.Type not same as T.Type

But you can declare a variable of type P.

Protocol should be a contract only, no instances of it.

I'm also confused about this. Tried to follow the whole discussion of experienced(as I understand) developer Charles and a member of core team, and saw that even experienced developer had some troubles to understand all the catches of protocols in Swift.

How we want to make less experienced developers to understand Swift's model of protocols&conformance&generic&existentials&etc.. While problems begins with the most used Equatable/Hashable protocols..

Also I believe the syntax of generic constraints in 'where' part is confusing. I'm about this:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element == Pepper

If '== Proto' means *exactly* typed as such protocol("let arr : [Proto] = .."), so IMO ': Proto' should means *typed* as exactly this protocol or any derived protocol("let arr : [ProtocolDerivedFromProto] = .."). Just in symmetry with class types in 'where' clause here.

Currently, how to specify 'protocol Proto or its derived protocols" ?

Basically, the issue is that as things currently stand, there is no way to specify that. Your options basically boil down to: 1) implement the method twice, 2) force the caller to cast to the protocol first, or 3) just use an array.

I suggest to discuss changing the rules for 'where' generic filter for protocols:

1. Don't allow syntax ': P' or '== P' for protocols. Such syntax should be allowed only for classes, structs & other value types.

2. For protocol introduce 'is P' syntax, which should mean 'typed exactly as P, or its derived protocol, or as concrete type conformed to P'. I.e.:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element is Pepper

3. If it is hard to implement (2) today in the "right" way, implement it with two copies of the same function, one with ":P" and one with "==P"(was discussed earlier in thread)

This looks identical to my pitch, only with the operator named “is” instead of “:==“. Unfortunately, we now have Word of God that the Swift team wants to avoid cloning functions à la C++ templates, so it may be a bit of a non-starter.

I’m not the Word of God, nor am I on the core team, I just work here and stating my opinions on the matter ;-)

Slava

···

On Aug 17, 2016, at 11:36 AM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 17, 2016, at 12:02 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On 17.08.2016 13:00, Boris Wang via swift-evolution wrote:

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The problem is that:
protocol should not be a type, but it is a type sometime and not type
sometime now.

for exam:
P.Type not same as T.Type

But you can declare a variable of type P.

Protocol should be a contract only, no instances of it.

I'm also confused about this. Tried to follow the whole discussion of experienced(as I understand) developer Charles and a member of core team, and saw that even experienced developer had some troubles to understand all the catches of protocols in Swift.

How we want to make less experienced developers to understand Swift's model of protocols&conformance&generic&existentials&etc.. While problems begins with the most used Equatable/Hashable protocols..

Also I believe the syntax of generic constraints in 'where' part is confusing. I'm about this:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element == Pepper

If '== Proto' means *exactly* typed as such protocol("let arr : [Proto] = .."), so IMO ': Proto' should means *typed* as exactly this protocol or any derived protocol("let arr : [ProtocolDerivedFromProto] = .."). Just in symmetry with class types in 'where' clause here.

Currently, how to specify 'protocol Proto or its derived protocols" ?

Basically, the issue is that as things currently stand, there is no way to specify that. Your options basically boil down to: 1) implement the method twice, 2) force the caller to cast to the protocol first, or 3) just use an array.

I suggest to discuss changing the rules for 'where' generic filter for protocols:

1. Don't allow syntax ': P' or '== P' for protocols. Such syntax should be allowed only for classes, structs & other value types.

2. For protocol introduce 'is P' syntax, which should mean 'typed exactly as P, or its derived protocol, or as concrete type conformed to P'. I.e.:

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element is Pepper

3. If it is hard to implement (2) today in the "right" way, implement it with two copies of the same function, one with ":P" and one with "==P"(was discussed earlier in thread)

This looks identical to my pitch, only with the operator named “is” instead of “:==“. Unfortunately, we now have Word of God that the Swift team wants to avoid cloning functions à la C++ templates, so it may be a bit of a non-starter.

Charles

···

On Aug 17, 2016, at 12:02 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:
On 17.08.2016 13:00, Boris Wang via swift-evolution wrote:

Working at Apple is close enough. ;-)

Charles

···

On Aug 17, 2016, at 3:25 PM, Slava Pestov <spestov@apple.com> wrote:

I’m not the Word of God, nor am I on the core team, I just work here and stating my opinions on the matter ;-)

Inline

The problem is that:
protocol should not be a type, but it is a type sometime and not type
sometime now.

for exam:
P.Type not same as T.Type

But you can declare a variable of type P.

Protocol should be a contract only, no instances of it.

I'm also confused about this. Tried to follow the whole discussion of
experienced(as I understand) developer Charles and a member of core team,
and saw that even experienced developer had some troubles to understand
all the catches of protocols in Swift.

How we want to make less experienced developers to understand Swift's
model of protocols&conformance&generic&existentials&etc.. While problems
begins with the most used Equatable/Hashable protocols..

Also I believe the syntax of generic constraints in 'where' part is
confusing. I'm about this:

func pick<PepperType:Sequence>(peppers: PepperType) where
PepperType.Iterator.Element == Pepper

If '== Proto' means *exactly* typed as such protocol("let arr : [Proto] =
.."), so IMO ': Proto' should means *typed* as exactly this protocol or
any derived protocol("let arr : [ProtocolDerivedFromProto] = .."). Just
in symmetry with class types in 'where' clause here.

Currently, how to specify 'protocol Proto or its derived protocols" ?

Basically, the issue is that as things currently stand, there is no way to
specify that. Your options basically boil down to: 1) implement the method
twice, 2) force the caller to cast to the protocol first, or 3) just use an
array.

Yes, we can force the caller to cast to "array of the protocol" first, and this is probably the best workaround here(if we want to keep sequence in our function):

func pick<PepperType:Sequence>(peppers: PepperType) where PepperType.Iterator.Element == Pepper {...}

let peck: [Pepper] = [PepperClass(), PepperClass()]
pick(peppers: peck) // no need to cast

let pickled = [PickledPepper()]
pick(peppers: pickled as [Pepper]) // cast here

I suggest to discuss changing the rules for 'where' generic filter for
protocols:

1. Don't allow syntax ': P' or '== P' for protocols. Such syntax should
be allowed only for classes, structs & other value types.

2. For protocol introduce 'is P' syntax, which should mean 'typed exactly
as P, or its derived protocol, or as concrete type conformed to P'. I.e.:

func pick<PepperType:Sequence>(peppers: PepperType) where
PepperType.Iterator.Element is Pepper

3. If it is hard to implement (2) today in the "right" way, implement it
with two copies of the same function, one with ":P" and one with
"==P"(was discussed earlier in thread)

This looks identical to my pitch, only with the operator named “is” instead
of “:==“. Unfortunately, we now have Word of God that the Swift team wants
to avoid cloning functions à la C++ templates, so it may be a bit of a
non-starter.

Well, yes, but also I believe we should disallow ': P' and '== P' syntax for protocols in 'where' for generic constants totally, as this syntax IMO is confusing for protocols and not in symmetry with what it means if class is specified as constraint instead of protocol.

I.e. if you want to specify a protocol in generic constants you can write only 'where T is P', which will mean "typed as P protocol, as derived from P protocol or as type conformed to protocol", I believe exactly this meaning is expected when one use protocol in constraints.

And about cloning the function. It is implementation detail and I believe core team can find better solution, but *at least* we see such naive implementation of this. So, probably, this could be done in such way and then later improved in right way with 'is P' syntax introduced.

···

On 17.08.2016 21:36, Charles Srstka wrote:

On Aug 17, 2016, at 12:02 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On 17.08.2016 13:00, Boris Wang via swift-evolution wrote:

Charles