> Am 09.06.2016 um 19:24 schrieb Austin Zheng via swift-evolution > <[email protected]>: > > On Thu, Jun 9, 2016 at 9:42 AM, Dave Abrahams via swift-evolution > <[email protected] <mailto:[email protected]>> wrote: > > on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote: > > > >> > >>> Introducing a language (not library) mechanism that exposes members on > >>> generalized existentials in a way that relies on runtime traps for > >>> type safety feels to me like a pretty dramatic turn agains the stated > >>> priority of safety. It will mean you must understand exactly what is > >>> going on and be extremely careful to use generalized existentials > >>> without causing crashes. This will either make Swift code much more > >>> crashy or will scare people away from using generalized existentials > >>> (and maybe both). > >> > >> I don't accept either of those statements without seeing some analysis > >> of the use-cases. For example, I don't believe that AnyCollection et al > >> are particularly crash-prone. The likelihood that you'll use the wrong > >> index type with a collection is very, very low. I'm less certain of > >> what happens with Self requirements in real cases. > > > > But again, I believe this is an exceptional case as the precondition > > is explicitly stated in the semantics of the protocol. > > IIUC, it has been cited by Doug as the exemplar of the > predominantly-requested case by a 10:1 ratio! > > At the risk of sounding waffly, I see both sides of the argument. > > Java is typesafe in the formal sense (can't perform an operation on a type > that doesn't actually support it); almost certainly moreso than Swift (which > lets you do wonderful things like reinterpret casting, C-style direct memory > manipulation...). > > However, in many cases Swift provides better compile-time assurances than > Java. In Java, any reference might actually be a null reference waiting to > cause a NullPointerException unless you explicitly check it in code (and what > if you forget to check it somewhere?). In Swift, (aside from IUOs and a few > other features) if your program compiles successfully the type checker has > proven that your software will never try calling a method upon nil. And if > you do use IUOs the postfix exclamation marks clearly mark spots in your code > that might cause trouble. > > In the same way I don't think trapping at runtime if an existential > satisfying a generic type parameter causes a type error is the best solution > to this problem. What we are doing is allowing a type that makes a weak > guarantee to stand in for a type that makes a strong guarantee, and catching > mismatches between those guarantees if/when they pop up. There's no easy way > for someone perusing the code to tell that such a mismatch might occur, > equivalent to IUO's postfix !. > > What I prefer is to explicitly "promote" the type with the weak guarantee to > a type with the stronger guarantee using the existential opening mechanism > that we've been throwing around. That way, it's very clear what is going on: > if the existentials in question can't serve as generic parameters to the > function you want to pass them to, you can fatalError() or do something else. > Or you can try force-opening with `as!` and mark a potential trouble spot in > the same way an IUO does. Once it succeeds you know you have the strong > guarantees that will cause your generic member to alway work the way it > should.
Yes, there should never be crashes for calling a wrong method. That is what *type* safety means. You still can have crashes when calling a method with wrong arguments. To make that type safe you would need a type system with dependent typing. I think that Swift should not only always be safe but that it should always be type safe, too. -Thorsten > > With the proposed mechanism, I expect that Any<Collection where .Element == > T> should be less prone to runtime trapping than AnyCollection<T>, because we > can reify the concept that each instance of the existential has a concrete > Index type associated with it, rather than relying upon a type-erased > AnyIndex shared among all AnyCollections. (That being said, I don't know if > AnyCollection is particularly problematic in practical use. I also don't know > if other type-erased wrappers users might define might be more or less prone > to this type of issue.) > > > > IMO the burden of proof should be on the side that proposes a > > mechanism to introduce traps, not the side that proposes avoiding > > them. > > If you really want to make this about sides and burdens, the burden of > proof always rests with the side proposing to extend the language. We > shouldn't be making changes without understanding how they will play out > in real use-cases. > > > Yes, I really want to see real-world use cases. > > >> > >> Neither of these look like they actually make *use* of the fact that > >> there's type erasure involved (and therefore should probably be written > >> as generics?). The interesting cases with Any<Collection...>, for the > >> purposes of this discussion, arise when you have multiple instances of > >> the same existential type that wrap different concrete types. > > > > One use case I have found is to work around the lack of higher-kinder > > types. > > Really, now: a use-case for feature A that is a workaround for the lack > of feature B hardly justifies adding feature A! We do want to add > higher-kinded types eventually. > > I just want to say, this made my day. > > Most of the examples in the document Matthew linked were meant to be > didactic, not practical. I would love to swap them out for more useful ones. > > > >> > >> Another problem I see: in this new world, what is the model for choosing > >> whether to write a function as a protocol extension/generic, or as a > >> regular function taking existential parameters? Given that either of > >> the above could have been written either way, we need to be able to > >> answer that question. When existentials don't conform to their > >> protocols, it seems to me that the most general thing to do is use > >> existentials whenever you can, and only resort to using generics when > >> forced by the type system. This does not seem like a particularly good > >> programming model to me, but I might be convinced otherwise. > > > > That doesn’t seem like a particularly good programming model to me either. > > > > The rule of thumb I am operating with for protocols with Self or > > associated type requirements is to prefer generics and use type > > erasure / existentials when that isn’t possible. For example, when > > heterogeneity is required or when you can’t form the necessary type in > > a protocol requirement (as in the preceding example). > > > > This heuristic has been working out pretty well for me thus far. > > I do worry a bit that people will choose the opposite heuristic. > > If we are to introduce this feature, I expect that the community would go > through another "reference vs value types - which is right for you?" type > discussion. It's true you can model a lot of things with classes where > structs would be more appropriate, but this doesn't seem to be a big issue. > > I would be very interested in affordances that can steer people towards using > generics when the dynamic nature of existentials isn't necessary. > > > > ______________________________________ > 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
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
