David Hart - Swift Generics - The 5 Stages of PATs

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] hello everybody my name is david hart by day i am the leader iOS developer in a small development shop in geneva called atopy and by night when I have free time I contribute to the Swift package manager' last year at I speak as we began preparations for a new iOS application for one of our clients we decided to transition to new architecture of choice we usually go for a light mix of MVC E or mvvm but the complexity of the project were working on forced us to introduce more rigor we consider formalizing our practices but in the end we needed an architecture that would naturally enforce better separation of concerns and improve our ability to debug issues in production so we research different architectures like Viper but in the end we went for Redux because it brought just the right amount of structure while staying conceptually very simple it also perfectly fit our need to for better logging and instrumentation as the architecture is originally from the world of Java scripts there aren't many Swift libraries out there we also wanted to try a different approach so we decided to roll our own library by the way a quick show of hands has anybody already work with redux here Oh Alfred nice but don't worry it's not a prerequisite for this talk at the core of redux there are only free principles there should be a single source of truth for the whole application that's our first roof should be encoded in a read-only state object and the only way to mutate that state it's a dispatch actions that trigger the change let's work on a very simple to-do app we define an app state to represent the whole application state it contains a dictionary of two do's and each of these two do's is represented by an added for identifier and a string of text to discourage developers from editing the state directly we represent it with structs and their value semantics make sure that it's clear to the developer using it that modifications won't propagate to the rest of the application to modify the application state we need to use actions and what better way to use to represent actions then with a protocol so each action implements a reduce function which mutates a state type but because this is a library and we don't know the state type in advance we need to use an associative type to represent it now let's define an action to create a new to-do item the action contains one property to configure the unique identifier and the reduce function creates that to do and adds it to the state object let's also create an action to edit a to-do text here we configure the action with both the identifier of the to do to edit and the new text and the reduce function simply sets the new text value let's put both of these actions together to define a helper function to duplicate a to-do by returning an array of actions it first creates a new to do and then sets its text to the same as the original hit build o compiler error let's see what it says protocol action can only be used as a generate constraint because it has self associative types requirements oh not this one again you've probably already experienced this error if you've ever designed or used for associated types the problem is that action depends on an Associated type state which can change depending on the conforming type here in the duplicate function the compiler simply can't infer what it will be and is so frustrating when that happens let me introduce you to the five stages of working with Pat's short for protocols with associated types we've got denial anger bargaining depression and finally acceptance since I first encountered these protocols with associated types and Swift's when I first look at the language a few years ago I was intrigued about similar and different they are two generics that I had encountered in other languages it took me a while to really understand them and how to best use them when designing api's this learning process took me through different stages of understanding which means which you can see up here if you've encountered this error before you're probably in one of those stages right now but don't despair reaching the final stage of acceptance is not so hard and brings a better understanding of how and why Swift is designed as it is making you a better developer let's begin when you see this error for the first time you might think you must be doing something wrong but no you're not dave abraham's finish foundational talk at WWDC 2015 about protocol oriented programming and swift introduced us to a new way to design a pis with hierarchies of protocols and protocol extensions well though if you've seen this presentation or not already I recommend you go and watch it again it's really good it's session 408 best of all it already points out the divide between protocol and protocols with associated types and the challenges of dealing with the latter this is not something new so if they're so difficult to work with why do we bother this leads us to the next emotional stage anger I'm never using protocols ever again okay this might be a bit extreme but let's see what this leads us Swift is a multi paradigm language protocol oriented programming is all the craves right now but it's not the only solution swift also has very good support for object-oriented programming so perhaps it's worth taking a step back when we were reaching for protocols to make sure there isn't a better solution for the task at hand so let's try fixing our Redux library by replacing protocols with class hierarchies the action protocol becomes a class with an override will reduce function and the state associated type is now a generic type to define an action we shall now subclass action and override the reduce function for example here's our set to do text action from the beginning as a subclass of action of appstate but unfortunately as we're using classes we don't get automatic initializer synthesis and we must explicitly define them so we've resolved our compilation error by getting rid of associated types but we've introduced extra boilerplate let's take a step back and ask ourselves our classes really the right choice in this case before we commit to the solution let's go back to Dave Abraham's WWDC talk to a slide where he discusses when to use classes we don't fit any of the scenarios in this slide we're not really using inheritance as we're using action as a kind of abstract base class and we're going out of our way to false value semantics by using final classes and let properties our solution works but it doesn't seem like classes are a natural fit perhaps in another scenario classes would have been a good approach but in our specific case I'm convinced that protocols and structs are the right tool can we come to an agreement in the compiler perhaps let's bargain with Swiss future this quote comes from a great blog post called Swift protocol wish list from fellow developer and app builder speaker Dave DeLong so I recommend you go read it it's a treasure trove of ideas for improving the status quo of protocols and Swift so let's ask the question what if protocols were generic if we go on the the Swift github repo we can find a document called the genrich's manifesto written by the swift compiler engineers to establish the list of potential language features around generics and there is a feature called generate protocols with generic protocols we could declare our action protocol with state as a generic type similarly if the standard library have been implemented using generic protocols instead of associated types we could declare a function to sort any collection of two do's more succinctly while the deck relation in today's Swift requires a constraints on the element associated type general protocols will allow us to declare it with a generic type nice right not exactly our sort function would also need to introduce a generic type I because collections are also paralyzed on the index type we don't care about the index type in our sort function yet it's polluting our generic declaration and that's why associated types are so useful they'll lie they allow hiding types behind and they relegate the parameterization of those types from the types user to the types implementer and that's probably why languages like Java or C sharp that don't have associated types don't go as far as making the index type of their collections generic but generic protocols are still a great idea languages like rust and Scala that have both associative types and generic protocols give us a glimpse of what is possible in Swift's future basically they allow types to conform to them multiple times and there are a great tool for building protocols around type conversions imagine a convertible from protocol so represent type conversions it's only a requirement is an initializer with a value of the Jarek type as only argument now let's imagine a type representing real numbers that type could conform to convertible from with floating and it could also conform to construct constructible from with double and that we have it we've got a system in swift to generalize over type conversions so if classes aren't always the correct choice and generic protocol will solve a different problem are we stuck there is a solution where it comes with a heavy cost welcome to depression this is another quote from Dave DeLong about our next topic gives you the state of mind that we're in let me introduce you to type erased wrappers this is the solution to working with Pat's and avoiding our compiler error but it comes with a high verbosity cost for both the API designer and user even if you've never heard of them you've probably already used them without knowing it the standard library is full of them and they all start with the any prefix any sequence any hashable any collection etc and they're Colt type erasers because they wrap instances of types that conform to a protocol and erase or hide the Associated type behind concrete or generic types okay let's write our own as a reminder here is our action protocol from the start step one start by declaring a struct with the associated types you're interested in as generic types in this case only state step to declare a private property for each protocol requirement and give it the same signature as the function in our case we declare the reduce closure property and typed it as a function from an out state to void then declare a generic initializer over a type conforming to the protocol and constrain its associated type to the rapper's generic type inside initialize each property with a closure that captures the original instance in our case we initialize the reduced closure with a literal closure that captures action and calls its reduce function finally implement each protocol requirement by calling the corresponding closure property and there you have it your very own type arrays wrapper this allows us to reemployment our duplicate function from the start of the talk we modify a signature to return an array of type arrays wrappers and we need to wrap our concrete actions in any actions so we finally resolved our original compiler error while keeping a design based on protocols and structs but unfortunately this solution is still far from ideal as an API designer we have to create those type erase wrappers and as an API user it forces us to constantly arap values inside them it feels like extra work and noise that we shouldn't be doing just to make the compiler happy but as it's the only solution we have all we can do is accept it I agree of Dave it would be really nice if the compiler could generate those type of race wrappers for us and I'm happy to tell you there is hope on the horizon if we go back to that generics manifesto document there is an interesting section towards the end which discusses the feature called generalized existential existential is just a fancy word to present types which abstract over other times for example codable is an existential type that represents instances of types that conform to encoder and decoder since we for we can also express existential x' that represent instances of a class or its subclasses that also conform to a protocol for example a uiviewcontroller that also implements UI table view delegate generalized existential x' is the feature that would allow us to generalize over those existential types by adding constraints to them for example here is how we could completely redefine the standard libraries any collection type by using generalized existential x' it's a collection where the elements associate type must equal our generic type similarly in the context of our to-do app we can now redefine any action with a simple type alias even better using using them becomes even simpler because we don't need to wrap the concrete value of any more the type system recognizes them as conforming to the existential now that's what I call a good solution to recap we've been on a journey to try to better understand and work with protocols of associative types a journey very similar to the one I went through we acknowledge that pats are indeed a pain to work with and it's not your fault we saw that they're not always the correct answer and that object-oriented programming is still a wonderful tool we looked at generic protocols but I hope I've convinced you that they can't completely replace Pat's we studied type raise wrappers as a solution to work around the current limitations of language and finally when it ended our journey on a more hopeful note for the future of Swift will feature called generalized existential the big question now is when when are generalized extensions coming to Swift while the core team probably can't make any promises on the release of any feature we can be reassured by the fact it seems quite high on their priority list if you want to follow up on this topic here are few resources the genrich's manifesto I mentioned several times during the talk is available on the Swift github repo and gives a good bird's-eye view of all the possible Swift generic features some of those features have already been implemented like conditional conformance --is that arrived in 4.1 while other features might never make it for good reasons several community members on the Swift evolution forums have also come up with a proposal for generalized existential it's quite old now so it's a bit outdated but it is still the most detailed look into this exciting feature if you under learn more about the differences between protocols with associated types and generic protocols the official book of the rust programming language has a very good chapter about it its equivalent feature just replace the word traits with protocol and you're good to go I would not generally link to a commercial product but the book advanced Swift from the folks at Objective C IO is so good that will be criminal not to do it and it contains a chapter that discusses type of race wrappers in detail including an alternative way to create them that there's more performance at the cloth at the cost of a little bit more complexity finally don't waste a to come talk to me during the conference my talk is now over I'd like to thank app builders for lying me to be here I'd also like to thank Mary and Todorov for being an invaluable help in preparing this talk and last but not least thank you for listening to me you you
Info
Channel: Swiss Mobile Developers Association
Views: 1,807
Rating: undefined out of 5
Keywords:
Id: _CJXGCaA2_4
Channel Id: undefined
Length: 22min 46sec (1366 seconds)
Published: Wed Apr 25 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.