Antoine Van der Lee

any Idea How to Use some Generics?

Generics
Transcript

0:00:03.8 Greg: We are very happy to have our next and first speaker of the day. Last time he came to Paris, it was for the Swift Paris Meetup back in January 2020, so just before that big thing, so we are really happy to have him back. He will talk about Generics. Please welcome Antoine van der Lee.

[applause]

0:00:29.7 Antoine Van Der Lee: Thank you, Greg. Are we on? Bonjour. Ça va ? Très bien ? Avez-vous de l'eau pour notre chien ?

[foreign language]

0:00:43.9 AL: Yeah, that's the French that I got to learn when I was on holiday with a big dog, you need some water when you're sitting on the terrace when you're chilling out. Super glad to be here. It's been a journey. This is in the early days of my career as a speaker, and I was doing call for papers and didn't get accepted. I sent it to several conferences, but yeah, here I am, and I guess it was a good opportunity to let you know, if you wanna do a talk, only 10% gets accepted or so, so don't give up. You commence it but I'm not here to give you any tips on career as a speaker. I wanna take you back to WWDC. I challenged myself to give a talk about a topic announced at WWDC and I only had two weeks for first conference to happen, and I wished to get some kind of guidance. Last year, it was async await. It was pretty clear, like the big announcement of the years. We had Codable, it was quite a big thing as well but this year, if you look at the Swift talks that we had, we had Embrace Swift Generics, and Design Protocol Interfaces in Swift.

0:02:07.5 AL: And looking into those proposals, those are all related to WWDC in general. I could see a highlight. There's opaque types, Existentials, Generics as well, and in a way, it felt like we're going back. This is also something that was merged for the Swift release, and Swift is open source. You can check that out, and what I found fascinating is the list of bugs that got fixed, and the code change was really small if you watch them, it was just two lines, I believe but yeah, it was Generics, protocols, opaque types, and Existentials, and they all have a reason that they start with working on that, especially now because Ben Cohen, this is the Swift version, the Output Cast episode 117 to be specific. I encourage you to listen to it as it gives some insights into the background story of the Swift release, but yeah, make Generics work naturally the way people expect it to work, and it's actually also the reason it's Swift 5.7 and not Swift 6. It's a bigger goal that they're reaching for to really make you develop apps without thinking how it should work, and it took me back, this is the Generics manifesto. Normally, I would ask who knows it, but I don't see many people, so it's fine.

0:03:37.8 AL: But the thing that's noticeable here is removing unnecessary restrictions, and that's like a red line in the story of Swift. So this is Swift 1.2, terrible time, no incremental builds and stuff. I've been there. But here we have a protocol, it's a content with an ID in a URL and a method, make favorite, and if you adopt that protocol, you had to implement that method individually in each instance. It's a lot of duplication. So what Swift 2.0 improved is to eventually allow extensions for protocols and remove unnecessary restrictions, and this is back in Swift 2.0, and that continue to happen. This is a quote from this year's What's New in Swift Session, WWDC. "Swift's goal is to make your life as developer easier. Once again, it's all about you, the experience of developing apps. Don't think about the restrictions. Make it easier. I'm not sure about you, but there's existential type, Generics, protocols, type erasure, associated types." There are so many things to think about.

0:04:42.5 AL: And I think it was Rob Napier at one of the earlier FrenchKit editions that did a talk on Generics, and a key message he said, "Generics is a tool. It's not mandatory to use it. You can also just duplicate a code if that's easier for you." So I guess my message now for today, I try to make it as easy as possible to understand but if you still feel overwhelmed after this talk, no worries. It takes time, it took me time as well, and yeah, I got articles that you can read easily later on. So let's dive in.

0:05:20.4 AL: I wanna start with the basics. We have Generics, here we have an in stack, which is not really generic. We have a push and a pop method but it only works with integers. So the first thing you can do, we create a stack with a generic element, and this way we can use it with any type that we want. And if you think about it, you're actually already using Generics. This seems like just an array of strings but if you look at the type, you can see that it's that stack that we created earlier, and it works the same with integers. You just change the type and it all works, and underneath you can reuse the same code.

0:06:06.4 AL: There's type constraints with Generics as well, here we are able to compare the value to the value to find, and it's all because we have the constraints set to be upgradeable, so any element that we wanna use with this method, you need to use upgradeable. And you can use it in different ways. Right? Here, we have an extension on array, where elements conforms to upgradeable, and this way we can find the index. Looking at the code implementation, they all look pretty similar. Right? This is the first method, then we have the Array Extension Method, and this is actually a method that's available today, just in the Swift default library, pretty much generic as well, and that allows Apple to remove a lot of duplicate code, and this is the nice thing, you can reuse this code when working with different types, so generic can really optimize the way you work with your own code.

0:07:05.4 AL: So how about opaque types? Let's dive in. I'm pretty sure you've all been using this code if you've written SwiftUI, but it might not be immediately clear like, "What does it actually do?" For the compiler, an opaque type is understandable, the compiler knows how big size of a memory it needs to make because it knows it's probably a VStack, or it's text, it can learn that from the code that you write in the body, and I'll explain that later on. One thing that I didn't do that often is using opaque types in different places, but you could actually do that already, so this is a make folder view with an SPrO Boolean, and we basically wanna change the returns elements based on the SPrO value.

0:07:55.8 AL: But you've might have seen this arrow popping up, and the reason is that the failure return, the type returns, is not consistent for the scope of the method. It's either a text or a VStack, as you can see here, and that way the compiler can't predict the outcome right, so it can't perform optimizations which are required for the opaque sum keyword. So you might, guys, let's just wrap it around the VStack, we create a consistent type, problem solved, but in fact, this is not what you want. In case of the SPrO is true, we now suddenly have a VStack and a text element, while we only needed the text, so one thing you can do is using the field builder, what's underneath is a result builder. You can read more about that, check out my article, and this way you can optimize your code to let Swift find out which type is returned in which case.

0:08:52.9 AL: Right, opaque types, how do they relate to Generics? So for that, I wanna take you on a journey. This is the WeTransfer app that I work on, my day-to-day job, and we have all kinds of content. Right? We have images, videos, PDF's, and we wanna create a method to favorite these content items, so to do that, we start with the protocol, or in this case, a structure with an ID and URL, and we create a favorite image content store. We have two methods, we have a method to favorite the content items and we have a method to read out whether something is favorite or not. The method used, it looks as follows, you just pass in an array of image content and we will save the ID's to ensure that we know what is favorite or not.

0:09:54.5 AL: But like I said, we also have video content, and as you can see here, it doesn't work with video content. The reason is that we have a specific image content store. So to solve that, we need to create those types and join them together, and we can do that by introducing the content protocol that we saw earlier. Basically, it contains the ID and the URL, and both the image content and video content conformed to it, so we rewrite our favorite image content store and we make use of the protocol. This actually worked in Swift 5.6, so before the WWDC 2022, yet, we wanna improve further. There's the identifiable protocol, which is pretty nice 'cause you can really make use of the code that's already in Swift, it also makes it easier to work with SwiftUI in several places, and we had an ID property, so this works out great.

0:11:01.9 AL: Yet, I think we've all been there. Right? I always felt this is an unnecessary restriction. Right? And you could actually see that in the WWDC session, they did like, "Poof!" And it just went away there. And that's a great thing of Swift 5.7, this is no longer a problem, and I'll explain to you how that works. First, this is how you would solve that in Swift 5.6, we use Generics, and we use the type constraints, and this way, we can still use this method with the protocol that comes with an associated type. This would be another way of writing it, using the workflows, but this is new in Swift 5.7. We now defined our parameter as some content, and this works, you don't get the error, and Swift is happy.

0:11:56.6 AL: So comparing these two methods. To me, I've started to realize the longer you work with Swift, the harder it might become to understand this. If you're just getting started, you would just learn about some content and the code might look much easier. Yet for me, if I look at the above line, it's much easier to understand than the constraints with the conforms to content and switch.

0:12:23.2 AL: You could say some protocol stands as a short end for T where T conforms to protocol, and if you keep that in mind, you could actually go over your existing code base and update it accordingly. You could say, "All we know is that there's gonna be some value of type content." So in summary, just like with Generics, your underlying type is fixed for the scope of the value. Right? Remember that method that I showed you earlier with a text element and a VStack? The same element has to be returned in all cases. If the generic parameter is only used in one place, you can use the opaque type by using this kind of like a mindset, use this as a shorthand for T, where T conforms to protocol. If you ask me, the code comes easier to read, so it might be a good improvement in terms of readability. We already covered a lot. There's more to come, and no worries, I'll have some practical examples in the end, so hopefully that will bring things all together and make it click.

0:13:30.8 AL: So let's dive into Existentials. So this is the image content story that we had before, and I now wanna zoom in on the favorite method. It comes with an array of multiple types, and here we still have a static type image content, so it's all predictable and you know there's no associated types of protocols and such, but once we use the protocol with the associated type ID, we start to see that error again.

0:13:57.4 AL: So you might think, "Let's use Generics again. Problem solved." You start using it, it still works but the problem here, we use consistent type in the scope of the method. We only have an array of image content, so this is predictable, but as soon as we swap out one of those image content, the compiler doesn't know how to predict this anymore and it can't perform any optimizations, so this doesn't work. So let's look at this method definition. Right? Let's use some, like I said, some has the requirement to have the same type in the scope of the method, and once more, we have image content and video content, so we can't use opaque types here. That's where any comes into place. A collection of any 10 kind of content. For me, when I learn code, I like to see how I would've solved it without it. Right? So before Existentials, how would I solve this?

0:15:01.1 AL: So let's look at a few examples how you can wrap around the heads around this. So here I created the structure, a content box, and the content box does the type ratio and we have the inner value. Yet, as you can see, we still use existential any here, so it's not really entirely what I was looking for. This could be another way to solve it, and we did this at WeTransfer actually, we had a few of these examples. What you do, you create like a new wrapper around the specific type and you erase the inner type and that way you also erase type relationships and such, so it could be that this is not really useful in all places, but you'll notice that soon enough. So it's any value but it conforms to content. Keep that in mind. I wanna shine a light on performance, and this is runtime performance. The fact that you use existential Any here means that you can change the type from image content to video content, but it does mean that a compiler doesn't know how to predict how much memory it needs. Right?

0:16:11.6 AL: So you start to use dynamic memory and that will decrease the runtime performance. I'm by no means an expert, but there are some threads on Swift, on Swift forums that you can look up when you go deeper into this topic, so if you're interested in this, go check that out. For opaque types, the compiler forces you to use the same type, which allows the compiler to do optimizations required, so it's better to always try to start with opaque types, and be aware because I noticed that Xcode 14 suggests you to use any where it's possible to use Some. I'll show you an example in the latest phase of this talk as well. So in summary, Any protocol stands for any value conforming to protocol. You no longer get that frustrated error message, which I think is beneath. You might not even notice that you will start using this later on, I wrote code before that I was like, "Why is this restricted?"

0:17:16.0 AL: It's gonna be enforced to use Any eventually, and this is to really make you notice that you opt into the performance hit. Right? So the Swift team wants you to know what you're doing with your code, and since it's unpredictable for the compiler, that's where the performance hit comes from. If you've watched the WWDC sessions you might recognize this slide, it's pretty much the same as what they did on the... I'm using code here, which I hope explains the difference between Some and any a bit more, so on the left side, you can see that we have fixed, concrete type. Right? It's two image content, so in the scope of that definition, it's a consistent type. That's why we can use Some. That's also why it guarantees type relationships. The compiler knows it's image content. On the right side, you can see that we have different types. We have image content and video content, and that way the compiler cannot ensure the type relationships as well 'cause it doesn't know what kind of content is in that area.

0:18:24.0 AL: This was I think a month ago trending on Twitter, and this is Ben Cohen from the Swift team. From the implementation side, it might look like you're using the same thing but the functions are really different, so it's really important to realize. Alright, so let's go into the practical examples. Whenever you learn something, it's always good to really get your hands dirty, try it out on your existing code base, start the projects yourself. Now you really get to understand how it works and how you can use it, so I'm gonna start with this print method. And what you can see here, we have a T conforming to protocol and we learned that some protocols tends as a shorthand for T conforming to protocol, so what you can do here, replace it with an opaque type, and the compiler is happy.

0:19:22.7 AL: Another scenario, there might be a few SDK developers here and the last thing you wanna do is expose all your codes publicly. Right? Any code that you expose in public needs to be mandate. So in this case, we have the remote image fetcher, which is public, but what if we want to rename remote image fetcher? That means that all the implementers need to update their code as well potentially, so that's not great. So what we can do is we create a new protocol image fetching where the methods fetch image and we make the remote image fetcher conform to this protocol. As you can see, remote image fetcher is no longer public and we can use it still in our image fetcher factory.

0:20:09.2 AL: But the SDK is multi-platform. We have macOS and we have iOS, so we wanna introduce an associated type image, and still we use the same logic for fetching an image. Since we have a remote image fetcher returning a UIImage, we now suddenly need to change this to use any, and this is suggested by Xcode 14, but as of now, because I'm not showing the inner body of the image fetcher for URL method, you're not sure yet whether it's actually needed to use any because it could very well be that inside the method definition, the type is the same, and that way we could use an opaque type. So let's see how the method body looks.

0:20:55.4 AL: In this case, we have two different types. We have a local image fetcher and a remote image fetcher, so the type in the scope of the method is not consistent and we need to use any. But in this case, we will always return a remote image fetcher, so the type is consistent and we can use opaque types. You can still run into errors, and in this case we created an extension, a UIImageView, and we wanna allow implementers to configure an image using the image fetching protocol that we defined earlier. But there is an associated type, which by this time is unclear what it will be. Will it be NSImage, UIImage, so what we can do, we can create a primary associated type. There's actually a proposal for that as well that you can look up. And then we can constrain that primary associated type to be conforming to UIImage.

0:21:58.1 AL: And this way, the configure image methods defined to UIImage view will always work with any protocol as long as the associated type is constrained to be a UIImage. You can imagine that you can reuse a lot more codes with this implementation. Yet, we have a consistent type, so we could actually use some in this case as well, so when I create my talks, I always wonder what would be questions you might have. This is one, and if I pronounce the title, it will be the same, but do we still need AnyView? Or can we use AnyView now? That's obviously the capital there, but in my experience, I never used AnyView. Not to say that it might be still useful, but I believe that in most of the cases which viewable and Generics should be able to solve most of the scenarios. Amd existential is just defining an extension container box like I showed with the alternative examples. Right? Side note on this, I found the answer in the Slack related to WWDC. There are so many questions in there that are super valuable. Yeah, I would highly encourage you to just browse through those questions, as you can learn quite a bit from actually Apple engineers answering those questions. This is another one, we have in any object. I went into this a bit more in detail in one of my articles, so I encourage you to check that out.

0:23:39.3 AL: So finally, I wanna wrap up, once you use some, any, or or Generics. I wanna give you like a guide when you write your code. Try to start with some, if a generic is only used in a single place, let the compiler tell you whether it's still working out or not. Change some to any when you get errors, there's probably a reason for it. If it clicks, you might understand why, but at that point you can always change some to any instead of starting with any from the get go, and if you have multiple type constraints, you can still fall back to Generics. We're still feeling overwhelmed. Can I get the light to see that? I'm curious. It's okay. I was overwhelmed still, the first time. Well, I guess it's a good sign, but like I said, it's not mandatory to directly go deep into this, start using this everywhere. It's pretty advanced. You can still duplicate the code. That's a lot easier to start with but yeah, I do encourage you to just read up on this, play around with it and see if you can get better at it. Regarding that, whenever I do a talk, I write all my content first in an article, so I got an article on pretty much everything you saw on this talk. I got a QR code later, so you can get the links easily.

0:25:07.8 AL: If you don't know what I do on the side yet, I got SwiftLee Jobs, SwiftLee and SwiftLee Weekly. SwiftLee Weekly is a newsletter. I could write articles every week that I find interesting enough to share with you and that I think are, yeah, pretty good to read. Not sure if you saw a tweet, and I went to bed wrong. I got a lot of messages on Twitter, which in a way is a good thing but I'm updating SwiftLee Jobs. I'm building a SwiftLee talent collective, so I can connect you with companies that I think are good enough, so if you're open for a job. And not actively searching, that's fine. It can be anonymous as well. Get in touch and, yeah, we'll see what we can do. This QR code didn't work when I tried it out from the audience. I'm curious if it works for you, otherwise There's a URL over there. This is a giveaway from a tool that I'm developing on the site. It's RocketSim. You can use like a grid and rulers, create images, quickly delete drive data, super useful. Yeah, go check it out. I'll show this slide again in a bit. This is brought with me as well so if you're a fan of this, go approach me. I got plenty, so not 250, by the way, but I've got enough, and that's it. Thank you very much.

[applause]

0:26:37.8 Simone: Thank you.

0:26:38.9 AL: Yeah, grab my water. Always hard to drink during a talk.

0:26:47.7 Greg: Yeah.

0:26:48.4 AL: I'll be too tall otherwise. Right?

0:26:53.1 Simone: We don't bite!

0:26:58.3 AL: Okay.

0:26:58.4 Greg: Only on demand.

[laughter]

0:26:58.6 Simone: Exactly, so we... First of all, actually, if you want to join the Q&A, ask questions, please use our FrenchKit hashtag and we'll make sure that some of the questions will be asked. We cannot make sure that all the questions will be asked, but at least some of them, so feel free to do that. I had a question which I think is a question that many of us have. Isn't Swift becoming too complex? I mean, when they announced Swift back in the day, they said, "This language is for like children and everybody can start learning Swift at age of four," and so on but now...

0:27:38.7 AL: Yeah, there's different angles of complex. I think all the new keywords are introduced for a reason. Right? They have the goal of making it easier for us, memory management, all those kind of trading issues that you have, so you could also argue that it's becoming easier. Yet, I see what you mean. Right? We have some, we have any, you have all those keywords, I guess you don't really have to know them all. There are some pretty advanced keywords there as well that you don't really have to use, so yeah, it's becoming more complex, but take it step by step, and yeah, do know that it's for the better.

0:28:14.0 Greg: Alright. Does all this have some impact, some visible impact on SourceKitServer performance? Like is it slower?

0:28:24.5 AL: Yeah, so last weekend, I was working on stock analyzer, one of my hobby projects, and I think I've been struggling for 20 minutes to find out where that error was coming from because it was at a whole different place, and then eventually I found out that I had to use like, well, opaque some instead of the whole list of Generics that I was writing. I guess what I tried to say is that the compiler doesn't really always know how to resolve syntax highlighting or where to put that error in the spot, so it's still early days for this change, I believe, and it's probably a short-term problem, so I guess that's what I experienced so far.

0:29:00.8 Simone: Yeah. Last technical question I had, is like any becoming a kind of the new weak, like you don't know what to do, you put weak and...

0:29:09.9 AL: Yeah. Well, I guess in some cases you're writing Existentials today as well, and what really the Swift team wants you to do is be aware of the performance that you're getting, and in my experience, the compiler helps you in Xcode 14. I find this much easier than weak self, let's say that.

0:29:30.0 Simone: Okay. Alright, thank you.

0:29:31.8 Greg: Are you expecting further evolution of the Generics?

0:29:36.1 AL: Yeah, so there's currently a roadmap to Swift 6 that you can find, and there's a few designs as well, I think, what was the... There is another manifesto that I showed, but then now for Swift 6, and they show I think move over keywords... So not a keyword. [laughter] Yeah, so there are a few changes coming up, which is mostly for memory managements and the long-term goal for Swift 6. Right? To make that all happen. So yeah, there are some changes coming up, but don't think about it yet, just focus on some and any.

0:30:09.2 Greg: Great. Thank you. We also saw that you are doing lots of things like jobs, for instance.

0:30:15.5 AL: Yeah. Sorry. Sorry.

0:30:16.4 Greg: That's interesting. [laughter] I have to think about that. How do you manage to find the time to work on all those topics?

0:30:24.4 AL: Yeah, yeah, I do a lot. I guess what key is for me, if you progress every day, even if it's five or 10 minutes, and you have a clear, prioritized planning, that you know what you wanna do the next time you get back to the side project, five minutes a day, that's like 35 minutes a week, and count it on and yeah, you start to progress, and that's what works for me. So I do little bits every day with clear planning and then you might get work. And don't start a new side project before you finish the other one.

[laughter]

0:31:01.3 Simone: We have some question from the audience actually, three or four, and you can of course read them using the #FrenchKit hashtag as well. I'll pick the last one: Is there a way to enforce Xcode, this version of Xcode to work like Swift 6 will?

0:31:19.4 AL: There is such setting for concurrency like you need to conform to send-able eventually as well, which allows you to do that right now before you run into Swift 6 and get all those warnings in Swift. I have not seen such selling yet for Existentials, though I think it should be fairly easy to adopt to Existentials if you are required to use them somewhere because it's just adding any in most of the cases.

0:31:46.6 Simone: Okay. Thank you very much.

Edit on GitHub