[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>
L Mihalkovic
laurent.mihalkovic at gmail.com
Fri Jun 17 15:08:14 CDT 2016
> On Jun 17, 2016, at 7:04 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>
>
> on Thu Jun 16 2016, Thorsten Seitz <tseitz42-AT-icloud.com <http://tseitz42-at-icloud.com/>> wrote:
>
>>> Am 13.06.2016 um 04:04 schrieb Dave Abrahams <dabrahams at apple.com>:
>>>
>>>
>>> on Fri Jun 10 2016, Thorsten Seitz <tseitz42-AT-icloud.com> wrote:
>>>
>>>>> Am 09.06.2016 um 19:50 schrieb Thorsten Seitz via swift-evolution <swift-evolution at swift.org>:
>>>>>
>>>>>
>>>>>> Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution at swift.org>:
>>>>
>>>>>>
>>>>>> on Wed Jun 08 2016, Jordan Rose <swift-evolution at swift.org> wrote:
>>>>>>
>>>>>>>> On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution
>>>>>>>> <swift-evolution at swift.org> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> on Wed Jun 08 2016, Thorsten Seitz
>>>>>>>
>>>>>>>> <swift-evolution at swift.org
>>>>>>>> <mailto:swift-evolution at swift.org>>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> Ah, thanks, I forgot! I still consider this a bug, though (will have
>>>>>>>>> to read up again what the reasons are for that behavior).
>>>>>>>>
>>>>>>>> Yes, but in the case of the issue we're discussing, the choices are:
>>>>>>>>
>>>>>>>> 1. Omit from the existential's API any protocol requirements that depend
>>>>>>>> on Self or associated types, in which case it *can't* conform to
>>>>>>>> itself because it doesn't fulfill the requirements.
>>>>>>>>
>>>>>>>> 2. Erase type relationships and trap at runtime when they don't line up.
>>>>>>>>
>>>>>>>> Matthew has been arguing against #2, but you can't “fix the bug” without
>>>>>>>> it.
>>>>>>>
>>>>>>> #1 has been my preference for a while as well, at least as a starting
>>>>>>> point.
>>>>>>
>>>>>> I should point out that with the resyntaxing of existentials to
>>>>>> Any<Protocols...>, the idea that Collection's existential doesn't
>>>>>> conform to Collection becomes far less absurd than it was, so maybe this
>>>>>> is not so bad.
>>>>>
>>>>> I think the problem is more that Any<Collection> does not conform to
>>>>> a specific value for a type parameter T: Collection
>>>>>
>>>>> What I mean by this is that `Collection` denotes a type family, a
>>>>> generic parameter `T: Collection` denotes a specific (though
>>>>> unknown) member of that type family and `Any<Collection>` denotes
>>>>> the type family again, so there is really no point in writing
>>>>> Any<Collection> IMO.
>>>>> The type family cannot conform to T because T is just one fixed member of it.
>>>>> It conforms to itself, though, as I can write
>>>>> let c1: Any<Collection> = …
>>>>> let c2: Any<Collection> = c1
>>>>>
>>>>> That’s why I think that we could just drop Any<Collection> and simply write Collection.
>>>>
>>>> Let me expand that a bit:
>>>>
>>>> Actually all this talk about existentials vs. generics or protocols
>>>> vs. classes has had me confused somewhat and I think there are still
>>>> some misconceptions present on this list sometimes, so I’ll try to
>>>> clear them up:
>>>
>>> There are several objectively incorrect statements here, and several
>>> others with which I disagree. I was hoping someone else would write
>>> this for me, but since the post has such a tone of authority I feel I
>>> must respond.
>>
>> You are right, the tone of my post was not appropriate, for which I
>> want to apologize sincerely.
>
> My fundamental disagreement is with the content, not the tone.
>
>> I still believe my statements to be valid, though, and will respond to
>> your arguments inline. Please don't get me wrong, I'm not trying to
>> have an argument for the argument's sake. All I want is to contribute
>> maybe a tiny bit to make Swift even better than it already is, by
>> sharing ideas and thoughts not only from me but from the designs of
>> other perhaps more obscure programming languages which I happen to
>> have stumbled upon in the past (often with much delight).
>
> And I want you to know, even though I disagree with what you've written,
> that I very much appreciate the contribution you're making.
>
>>>> (1) misconception: protocols with associated types are somehow very
>>>> different from generics
>>>>
>>>> I don’t think they are and I will explain why. The only difference is
>>>> the way the type parameters are bound: generics use explicit parameter
>>>> lists whereas protocols use inheritance. That has some advantages
>>>> (think long parameter lists of generics) and some disadvantages.
>>>> These ways are dual in a notation sense: generic types have to have
>>>> all parameters bound whereas protocols cannot bind any of them.
>>>> The „existential“ notation `Any<>` being discussed on this list is
>>>> nothing more than adding the ability to protocols to bind the
>>>> parameters to be used just like Java’s wildcards are adding the
>>>> opposite feature to generics, namely not having to bind all
>>>> parameters.
>>>
>>> Protocols and generics fulfill completely different roles in Swift, and
>>> so, **especially in a language design context like the one we're in
>>> here**, must be thought of differently. The former are an abstraction
>>> mechanism for APIs, and the latter a mechanism for generalizing
>>> implementations.
>>
>> That's not what I was talking about. Of course, protocols are a
>> mechanism for deriving types from each other whereas generics are a
>> way to parameterize types. My point was that Swift's other way to
>> parameterize types, namely by associated types, is very similar to
>> generics with wildcards when looking a the existentials of such
>> protocols. In addition I was talking about generics in general, not
>> just about generics in Swift which restricts them to implementations
>> and does not support wildcards.
>
> I'm aware of these other systems. One of the problems with the way
> you're writing about this is that we're speaking in the context of Swift
> and you're assuming a completely open design space, as though Swift's
> choice to sharply distinguish classes from protocols was not a conscious
> one... but it was. Yes, Swift could have been designed differently, so
> that a single language construct, a kind of generic class, was stretched
> so it could express almost everything. Personally, I don't believe that
> results in a better language.
>
>> Other languages like Java offer generics for interfaces as well and
>> support wildcards (adding generic types parameters to protocols in
>> Swift is currently discussed on the mailing list as well). FWIW my
>> arguments were not about whether we should have wildcards in Swift or
>> not, but simply to relate one parametrization feature (associated
>> types) to a more well known parametrization feature (generics with
>> wildcards) in order to understand them better.
>>
>>> The only place you could argue that they intersect is
>>> in generic non-final classes, because a class fills the dual role of
>>> abstraction and implementation mechanism (and some might say that's a
>>> weakness). But even accounting for generic classes, protocols with
>>> associated types are very different from generics. Two utterly
>>> different types (an enum and a struct, for example) can conform to any
>>> given protocol P, but generic types always share a common basis
>>> implementation.
>>
>> The latter is not the case for generic interfaces in Java, for
>> example, so it is just an artificial restriction present in Swift.
>
> It's not an artificial restriction, it's a design choice. Sure, if by
> “generic type” you just mean anything that encodes a static type
> relationship, lots of things fall into that bucket.
>
>>> There is no way to produce distinct instances of a generic type with
>>> all its type parameters bound,
>>
>> That is true in Swift (except for generic classes) due to the
>> restriction just mentioned.
>>
>>> but for any protocol P I can make infinitely many instances of P with
>>> P.AssociatedType == Int.
>>
>> This likewise applies to generic interfaces and for generic types in
>> general if taking inheritance into account - just like you do here for
>> protocols.
>>
>>> Back to the my original point: while protocols and generic types have
>>> some similarities, the idea that they are fundamentally the same thing
>>> (I know you didn't say *exactly* that, but I think it will be read that
>>> way) would be wrong and a very unproductive way to approach language
>>> evolution.
>>
>> I said that protocols *with associated types* are much like generics
>> *with wildcards* and tried to show why.
>
> If all you're trying to do is say that there's an analogy there, then we
> have no argument.
>
>>>> Essentially `Any<Collection>` in Swift is just the same as
>>>> `Collection<?>` in Java (assuming for comparability’s sake that
>>>> Swift’s Collection had no additional associated types; otherwise I
>>>> would just have to introduce a Collection<Element, Index> in Java).
>>>
>>> I don't see how you can use an example that requires *assuming away*
>>> assoociated types to justify an argument that protocols *with associated
>>> types* are the same as generics.
>>
>> Note, that I said *additional* associated types, i.e. in addition to
>> .Element, even giving an example how the Java interface had to be
>> extended by a type parameter `Index` if this assumption was not
>> applied (still simplifying because Generator would have been more
>> correct which would have to be added as type parameter in addition to
>> `Index`).
>>
>> So, in essence the comparison is between the following (I'm using Foo
>> now instead of Collection to avoid the differences mentioned. Note
>> that this has no impact on the argument at all):
>>
>> protocol Foo {
>> associatedtype T
>> ...
>> }
>>
>> interface Foo<T> {
>> ...
>> }
>
> Yes, those correspond.
would be difficult to say otherwise ;) considering:
"In Swift, I suggest that we use the term protocol for this feature, because I expect the end result to be similar enough to Objective-C protocols that our users will benefit, and (more importantly) different enough from Java/C# interfaces and C++ abstract base classes that those terms will be harmful. The term trait comes with the wrong connotation for C++ programmers, and none of our users know Scala."
>
>> My argument is that existentials of protocols with associated types
>> are just like generic types with wildcards, i.e. `Any<Foo>` in Swift
>> is just the same as `Foo<?>` in Java.
>> Likewise `Any<Foo where .T: Number>` is just the same as `Foo<?
>> extends Number>` in Java. For me that was an insight I wanted to
>> share.
>
> It's a good one.
>
>>>> And just like Collection<?> does not conform to a type parameter `T
>>>> extends Collection<?>` because Collection<?> is the type `forall
>>>> E. Collection<E>` whereas `T extends Collection<?>` is the type
>>>> `T. Collection<T>` for a given T.
>>>>
>>>> In essence protocols with associated types are like generics with
>>>> wildcards.
>>>
>>> It is true that generics with wildcards in Java *are* (not just “like”)
>>> existential types but I don't agree with the statement above. Because
>>> Java tries to create an “everything is a class” world, generic classes
>>> with bound type parameters end up playing the role of existential type.
>>> But protocols in Swift are not, fundamentally, just existential types,
>>> and the resyntaxing of ProtocolName to Any<ProtocolName> for use in type
>>> context is a huge leap forward in making that distinction clear... when
>>> that's done (unless we leave Array<ProtocolName> around as a synonym for
>>> Array<Any<ProtocolName>>—I really hope we won't!) protocols indeed
>>> *won't* be types at all, existential or otherwise.
>>
>> I fully agree that protocols are not types, their existentials
>> are. But I haven't seen yet what we really *gain* from making that
>> distinction explicit (except an ugly type syntax :-).
>
> For me, it helps distinguish static from dynamic polymorphism.
>
>> And like I already wrote in this or another thread we would have to
>> apply the same logic to non-final classes, which are existentials,
>> too.
>>>
>>>> Coming back to the questions whether (a) allowing existentials to be
>>>> used as types is useful
>>>
>>> That's the only use existentials have. They *are* types. Of course
>>> they're useful, and I don't think anyone was arguing otherwise.
>>
>> I'm pretty sure that there was a discussion about whether being able
>> to write something like Any<Collection> is useful. My wording was
>> certainly imprecise, though, and didn't make sense as written. I
>> should have said something like "whether adding the ability to use
>> existential types of protocols with unbound associated types is
>> useful".
>>
>>>
>>>> and (b) whether sacrificing type safety would somehow be necessary for
>>>> that, I think we can safely answer (a) yes, it *is* useful to be able
>>>> to use existentials like Any<Collection> as types, because wildcards
>>>> are quite often needed and very useful in Java (they haven’t been
>>>> added without a reason) (b) no, sacrificing type safety does not make
>>>> sense, as the experience with Java’s wildcards shows that this is not
>>>> needed.
>>>
>>> I would call this “interesting information,” but hardly conclusive.
>>> Java's generics are almost exactly the same thing as Objective-C
>>> lightweight generics, which are less capable and less expressive in
>>> many ways than Swift's generics.
>>
>> I agree that Java does not have something like `Self` or associated
>> types (which are really useful for not having to bind all type
>> parameters explicitly, especially when binding type parameters to
>> other generics which makes for long type parameter lists in Java where
>> I have to repeat everything over and over again), but do you mean
>> something else here?
>> Especially in the context of sacrificing type safety?
>
> I do, but it will take some research for me to recover my memory of
> where the holes are. It has been years since I thought about Java
> generics. It's also possible that I'm wrong ;-)
>
>>>> Especially if something like path dependent types is used like
>>>> proposed and some notation to open an existential’s type is added,
>>>> which is both something that Java does not have.
>>>>
>>>> (2) misconception: POP is different from OOP
>>>>
>>>> It is not. Protocols are just interfaces using subtyping like OOP has
>>>> always done. They just use associated types instead of explicit type
>>>> parameters for generics (see above).
>>>
>>> They are not the same thing at all (see above ;->). To add to the list
>>> above, protocols can express fundamental relationships—like Self
>>> requirements—that OOP simply can't handle.
>>
>> Eiffel has something like Self, it is called anchoring and allows
>> binding the type of a variable to that of another one or self (which
>> is called `Current` in Eiffel). And Eiffel does model everything with
>> classes which may be abstract and allow for real multiple inheritance
>> with abilities to resolve all conflicts including those concerning
>> state (which is what other languages introduce interfaces for to avoid
>> conflicts concerning state while still failing to solve *semantic*
>> conflicts with the same diamond pattern).
>> No protocols or interfaces needed. Why do you say this is not OOP? The
>> book which describes Eiffel is called "Object-Oriented Software
>> Construction" (and is now about 20 years old).
>
> It's not *incompatible* with OOP, but it is not part of the essence of
> OOP either. If you survey object-oriented languages, what you find in
> common is inheritance-based dynamic polymorphism and reference
> semantics. Those are the defining characteristics of OOP, and taking an
> object-oriented approach to a given problem means reaching for those
> features.
>
>>> There's a reason Java can't
>>> express Comparable without losing static type-safety.
>>
>> You are certainly right that Java is not the best language out there
>> especially when talking about type systems (I often enough rant about
>> it :-) but I'm not sure what you mean here. Java's Comparable<T> seems
>> quite typesafe to me. Or do you mean that one could write `class A
>> implements Comparable<B>` by mistake? That's certainly a weak point
>> but doesn't compromise type safety, does it?
>
> Java has cleverly avoided compromising type safety here by failing to
> express the constraint that comparable conformance means a type can be
> compared to itself ;-)
somehow considering how dynamic java is, it would have been short-sighted to express the constraint in the type itself rather than as it is, in generic methods where it can be guaranteed.
public static <T extends Comparable <http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html><? super T>> void sort(List <http://docs.oracle.com/javase/6/docs/api/java/util/List.html><T> list)
>
>> Ceylon has an elegant solution for that without using Self types:
>>
>> interface Comparable<in Other> of Other given Other satisfies Comparable<Other> {...}
>>
>> Note the variance annotation (which Swift currently has not) and the
>> `of` which ensures that the only subtype of Comparable<T> is T. This
>> is a nice feature that I haven't seen often in programming languages
>> (only Cecil comes to mind IIRC) and which is used for enumerations as
>> well in Ceylon. In Swift I cannot do this but can use Self which
>> solves this problem differently, albeit with some drawbacks compared
>> to Ceylon's solution (having to redefine the compare method in all
>> subtypes,
>
> That sounds interesting but is a bit vague. A concise example of how
> this plays out in Swift and in Ceylon would be instructive here.
>
>> which has lead to lengthy discussion threads about Self, StaticSelf,
>> #Self etc.).
>>
>>> Finally, in a
>>> language with first-class value types, taking a protocol-oriented
>>> approach to abstraction leads to *fundamentally* different designs from
>>> what you get using OOP.
>>
>> Eiffel has expanded types which are value types with copy semantics
>> quite like structs in Swift. These expanded types are pretty much
>> integrated into Eiffel's class-only type system. Just define a class
>> as `expanded` and you are done.
>
> Unless this part of the language has changed since 1996, or unless I've
> misread https://www.cs.kent.ac.uk/pubs/1996/798/content.pdf <https://www.cs.kent.ac.uk/pubs/1996/798/content.pdf>, you can't
> make an efficient array with value semantics in Eiffel. That, IMO,
> cannot be considered a language with first-class value types.
java/jvm is about to throw an interesting wrench into the status-quo. Between the different forms of non-ref based arrays that can be optimized down to the JVM (azul is ahead of everyone in that arena) and value types, a lot of preconceived notions will have to go away (which I fully expect they won’t anytime soon considering how we like to hold on to things).
>
>> Eiffel seems to have no need to introduce interfaces or protocols to
>> the language to support value types.
>
> No, of course not. By saying that everything from abstract interfaces
> to static constraints and even value types is to be expressed a kind of
> possibly-generic class, you can eliminate distinctions in the language
> that IMO help to clarify design intent. This is a language design
> choice one could make, but not one I'd want to. In LISP, everything is
> an S-expression. That has certain upsides, but for me it fails the
> expressivity test.
>
>> You can even derive from expanded classes which is currently not
>> possible in Swift but has already been discussed several times on this
>> mailing list. Polymorphic usage is only possible for non expanded
>> super types, which means as far as I understood that a reference is
>> used in that case. Variables with an expanded type do not use refences
>> and therefore may not be used polymorphically in Eiffel. This should
>> be similar in Swift, at least as far as I did understand it. The
>> question whether variables with a value type can be used
>> polymorphically currently does not arise in Swift as structs cannot
>> inherit from each other (yet?).
>>
>>>
>>>> The more important distinction of Swift is emphasizing value types and
>>>> making mutation safely available by enforcing copy semantics for value
>>>> types.
>>>
>>> We don't, in fact, enforce copy semantics for value types. That's
>>> something I'd like to change. But regardless, value types would be a
>>> *lot* less useful if they couldn't conform to protocols, and so they
>>> would be a lot less used. Heck, before we got protocol extensions in
>>> Swift 2, there was basically *no way* to share implementation among
>>> value types. So you can't take protocols out of the picture without
>>> making value types, and the argument for value semantics, far weaker.
>>
>> Why? Like I said, Eiffel *has* value types without needing
>> protocols. They just have a unified mechanism built around classes.
>
> Because I'm speaking about Swift, not some other world where Protocol ==
> Generic Class ;-)
>
> --
> -Dave
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160617/fb306e11/attachment.html>
More information about the swift-evolution
mailing list