[swift-evolution] PITCH: New :== operator for generic constraints

Boris Wang kona.ming at gmail.com
Wed Aug 17 05:00:32 CDT 2016


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 at swift.org>于2016年8月17日
周三14:11写道:

> On Aug 17, 2016, at 12:35 AM, Slava Pestov <spestov at apple.com> wrote:
>
>
>
> On Aug 16, 2016, at 10:16 PM, Charles Srstka <cocoadev at charlessoft.com>
> wrote:
>
> On Aug 16, 2016, at 11:42 PM, Slava Pestov <spestov at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160817/21d34e69/attachment.html>


More information about the swift-evolution mailing list