Regards (From mobile)
> On Jun 25, 2016, at 6:34 PM, Thorsten Seitz via swift-evolution > <[email protected]> wrote: > > Sorry for the late reply — I had hoped to be able to think more deeply about > various points, > but I’m going to delay that instead of delaying the reply even more :-) > > >> Am 17.06.2016 um 19:04 schrieb Dave Abrahams <[email protected]>: >> >> >> on Thu Jun 16 2016, Thorsten Seitz <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. > > Thanks! I’m very glad about that! > > >> >>>>> (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. > > I never had assumed that this had been decided lightly ;-) > And I have been favorably impressed by the rationales put forth so far by the > Swift > team, so it would definitely be interesting to learn a bit more about the > rationale > being put into that decision and the advantages and disadvantages discussed > back then. > Is there something written down somewhere? > I think some applicable rational exist in type-system papers that came out of studying scala's. >> 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. > > I still believe it would have advantages but I’ll concede that this > discussion > will probably not help advancing Swift as this decision has been made. > Still, it might be of interest to keep in mind for further design > considerations. Somehow the world of languages is small, and tracing inspiriation across is playing permutations on a limited set. > >> >>> 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 > > I meant artifical in the sense that a different design choice would have been > possible. > > >> “generic type” you just mean anything that encodes a static type >> relationship, lots of things fall into that bucket. > > Well, I think Java’s generics are not that advanced, so the bucket does not > have to be very big :-) I am curious to see what language you have in mind when you are making a comparison? > > >> >>>> 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. > > Ok. > > >> >>>>> 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. >> >>> 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. > > Thanks! > > >> >>>>> 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. > > Hmm, I’ll have to think more about that. > > >> >>> 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 ;-) > > If you happen to remember, I’d be interested in hearing about the problems > you meant. > > >> >>>>> 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. > > Agreed, it is not part of most OOP *implementations* while being compatible > with OOP. > There have been lots of papers and research languages about typing problems > like > binary methods, null pointers etc., though, so taking the mainstream OO > languages > as the yardstick for OOP is jumping a little bit too short IMO. > > >> >>>> 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 ;-) > > Indeed :-) > > >> >>> 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. > > Sorry, the difficulty with Self I was thinking of only occurs when Self is in > a covariant position > which is not the case in Comparable, of course. Let’s take a modified example > instead with Self > in a covariant position: > > Swift: > > protocol Minimizable { > func min(from other: Self) -> Self > } > > final class A : Minimizable { // has to be final > > let x: Int > > init(x: Int) { > self.x = x > } > > func min(from other: A) -> A { > return x < other.x ? self : other > } > } > > Ceylon: > > interface Minimizable<Other> of Other given Other satisfies > Minimizable<Other> { > shared formal Other min(Other other); > } > > class A() satisfies Minimizable<A> { > > Integer x = 0; > > shared actual default A min(A other) { > if (x < other.x) { > return this; > } else { > return other; > } > } > } > > In Ceylon class A does not have to be final and choosing the minimum of two > values would be available for values from the whole subtree of types rooted > in A (the `of` ensures that such a declaration cannot „cross“ into other > subtrees) whereas `Self` enforces that there is no subtree below class A. > > I have to admit that I am not well versed using `Self`, yet, so maybe I’m > wrong here. In addition I am sure that `Self` allows designs > that are not possible with Ceylon’s `of`. > > The usage of Ceylon’s `of` for enumeration types is as follows (example taken > from http://ceylon-lang.org/documentation/tour/types/): > > abstract class Node() of Leaf | Branch {} > > class Leaf(shared Object element) > extends Node() {} > > class Branch(shared Node left, shared Node right) > extends Node() {} > void printTree(Node node) { > switch (node) > case (is Leaf) { > print("Found a leaf: ``node.element``!"); > } > case (is Branch) { > printTree(node.left); > printTree(node.right); > } > } > > > >> >>> 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, you can't >> make an efficient array with value semantics in Eiffel. That, IMO, >> cannot be considered a language with first-class value types. > > I haven’t had time yet to really evaluate that paper, but if you are right, > then I agree > with you that Eiffel cannot be considered as having first-calss value types. > > At least one of the deficiencies listed in the paper does not exist anymore > AFAIU > (expanded types not having constructors), though, so maybe things actually do > have > changed since then. > > >> >>> 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. > > That’s certainly a valid point. > > Furthermore I do understand (and fully support) that being interoperable with > Objective-C is an > important restriction on Swift’s design space and I think it is absolutely > awesome how > that has been achieved! > > >> >>> 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 ;-) > > Ah, ok, I took your statement of protocols being needed for strong value > semantics > to be of general validity, not confined to Swift :-) > Within Swift that is certainly true! > > -Thorsten > > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
