> On Jun 17, 2016, at 7:04 PM, Dave Abrahams via swift-evolution > <[email protected]> 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 <[email protected]>: >>> >>> >>> 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 >>>>> <[email protected]>: >>>>> >>>>> >>>>>> Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution >>>>>> <[email protected]>: >>>> >>>>>> >>>>>> on Wed Jun 08 2016, Jordan Rose <[email protected]> wrote: >>>>>> >>>>>>>> On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>>>>>>> <[email protected]> wrote: >>>>>>>> >>>>>>>> >>>>>>>> on Wed Jun 08 2016, Thorsten Seitz >>>>>>> >>>>>>>> <[email protected] >>>>>>>> <mailto:[email protected]>> >>>>>>>> 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 > [email protected] <mailto:[email protected]> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
