Vincent Pradeilles

Swift Quiz

Transcript

0:00:57.2 Vincent Pradeilles: Good afternoon. I hope you still have a little bit of energy for this last talk of FrenchKit. So is your title Swift quiz. I have three questions for you and hopefully they should give you some nice ideas that you'll be able to apply when you'll be, when you will be writing code again next Monday hopefully. So let's get started with the first question, which is going to be about closure, capture list and retain cycle. So let's see some code. So you can see a view controller. You can see the controller has some properties. We have a service, a formatter, a label. So it's a simplified version of something that we are all familiar with I think. We have a set of AnyCancellables because we are using Combine here. And then inside viewDidLoad, you can see that we are making a code on the service and we are _sink_ing on that code. And if we focus on the closure, we pass to the method .sink. You can see that inside of it there are no optionals.

0:02:01.0 VP: The formatter and the label are both non-optional strong references. So we have some very nice ergonomics here. But of course the big question is will this code leak instances of the view controller? So we are going to do a vote actually. So if we can have some light on the audience so that I can see how it is split.

0:02:27.2 VP: So we are going to do it by a show of hands. You can stand up if you feel very energized. So if you think that this will leak and will create a written cycle, raise your hand. Okay, we have a few hands raising. Okay. If you think it won't create a written cycle and won't leak, raise your hand. Okay. So there are more people for "no", so that's good because "no" is the correct answer and let's try to understand why. So let's try to draw the memory graph of this code. So we have the view controller that owns both the formatter, the label, and the cancellables with strong references. Then the cancellables through some other objects owns the closure. And the closure retains only strong references of the formatter and label, but not of the view controller. So indeed, there are no cycles here and this code will not generate a written cycle. But now, so indeed capturing properties rather than serve inside the closure won't create a written cycle. But the big question is why is weak self the go-to solution when this syntax seems to arguably be better? Like could there be some trap?

0:03:45.0 VP: And indeed there is a trap here and it is that with this syntax you capture a strong reference of the formatter on the label, but you capture the reference the variables were holding when the closure was created. So if they were to be updated later in time, the closure would still reference the older versions. So here it's not a real risk because these are let constants, so they cannot be updated. But if you're using a var please note that these could introduce a bug. Please also note that if at a later time the lead gets turned into a var you won't be warned by the compiler that the closure has now become unsafe. So it can be a nice trick sometimes if a weak self is becoming annoying but they're both short so use carefully, no refunds.

0:04:31.9 VP: Let's move to the second question, which is about constant properties and mutating methods and value types. I forgot to write that one. So once again, let's introduce some code. So we have a strike code Person, I'm storing an age in a let property, and then I have defined a mutating method called .incrementAge(). And I want to ask you could we increment the property age inside that method? And what I like with this question is that if you don't know the answer that you could say, balancing power that here, if you try to infer it, on the one hand you have a let property which arguably should be immutable, but on the other hand you have a mutating method and maybe this mutating keyword could somehow make this work.

0:05:19.0 VP: So once again, let's put some light on the audience and let's do a vote. So raise your hand if you think it is possible to increment the age property with this method. Okay, I can see a few hands. So that was for yes, thank you. [chuckle] And for "no", if you think it is not possible. Okay, so it seems there are a little bit more of a no, which is interesting because the good answer is actually "yes", but it's not as straightforward as we might think because of course this code, the obvious code, doesn't work. So it's more like, is there a way so that at the end of the method, the property age will add value that has been incremented? Let me explain what I mean.

0:06:00.4 VP: And for that, I'm going to write some code outside of the method. For instance, I'm going to create a value of that type person. I need to store it somewhere. I'm going to store it in a variable, I'm going to make it var because if I want to code a mutating method, I need it to be a var later on. So on this var, I could code my mutating method, but since it's a var, I can also do other things: for instance, I can assign a new value to it. I can assign a new value of that person. And when I assign that new value for the argument age, I can read the current value and increment. So when we have a var we can make it work. Now the question is, could we take this last line of code and make it work in the context of the mutating method? And as it turns out, we can. So first, from where do we read the current age? Pretty straight forward we read from self, and then where do we write the new value? Same answer actually. We will write it on self because it is possible to set a new value to self inside a mutating method of value type. And this is a bit of a trick. So now if we call mutating method at the end, it will indeed have somewhat indirectly, but still incrementing the property age, at least from the point of view of the call site, from the caller that is what will have happened. And so this technique is known as reassigning self. Let's give credit where it is due: I learn about it on objc.io website - I usually recommend you checkout if you've never checked it out. And keep in mind that it can result in some tricky code because it allows assigning, when you code a mutating method on variable, it can potentially assign a new method to it without having an explicit assignment operator at the call site. And is bit of trivia, but for instance, if you're working with externalized dec case for which we don't have the code source, keep in mind that they can do that from under you at any point.

0:08:00.4 VP: And now we move on to the third and final question, which is about Empty enums. So let's once again have a look at a very weird piece of code, I'm defining an enum that is empty. And then I define a function called magic, a generic function, that takes an argument of type Empty and returns a value of type T.

0:08:22.7 VP: And the Empty implementation is here on purpose. It's not missing, it's not like abbreviated. This is the code. And I want to ask you once again, by show of hands, do you think that this code builds? And once again, just like before we have two like competing powers here, want to infer the answer. On the one hand, this seems to be wrong because there isn't even a return statement inside this method. But on the other hand, the argument is just so weird. Could it make it that there is something special here? Once again, by show of hands, if we can see the audience. Raise your hand if you think that this code does build. Okay, few hands up being risen. And raise your hand if you think it does not build. Okay, slightly less, but we are still kind of split.

0:09:11.2 VP: The answer is that this code builds. And what's really interesting is why it builds because there is math behind it. For that, I'm going to show you a piece of course will look absolutely different. It will look like there's nothing to do, but bear with me. It has everything to do. We find once again ourselves with our previous choice person. And I'm doing something weird with it. I'm taking an empty Array and I'm calling the method .allSatisfy on it with the predicate that the age must be greater than 18. And if you run that code, you will see that this does not return nil, this does not return false, it returns true. And it's not a bug, it's not an approximation. This is actually mathematically sound. This is called a Vacuous Truth. And the logic here is that since the array is empty, it's impossible to find a counter example that proves the property false.

0:10:06.3 VP: And since it cannot be proven false, then it must be true. And the compiler follows the same logic for our method, for our magic function. So let's write it down again. Since Empty is a type that processes no instances, it is impossible to create a valid call site for this method. Only thing you could do would be a force cast that will always crash. And so since it's impossible to have a valid call site, all possible bodies are valid. And so we don't need to provide one. The compiler knows the body will never be executed and it accepts an empty implementation as valid. And this seems very like a piece of trivia, but it's actually something that the Swift compiler relies a lot on because this empty enum does exist in the Swift in that way, it's not called "Empty" it's called Never and you can do some pretty interesting things with it.

0:11:01.3 VP: For instance, if some day you need to con form to protocol, it expects either a Result or a Publisher. And you know that in your very special case, the Publisher or the Result cannot produce an error, you can use Never for the generic argument error. And you will see that in that case, the compiler will not force you to implement the .failure case. And it's not a bug. The compiler still checks for exhaustivity, but it knows that, since the failure case can never be executed, it is an impossible code path. And so the compiler won't expect you to write the code for it. Also, a very interesting place where the Never type pops up, is when you call fatalError(). And it might seem a bit weird because after all, fatalError() is a function is about stopping the of the program. But fatalError() does have a return type. And guess what? That return type is Never. And it lets the compiler know that every code that is after this method is dead code. And this is how you can have some nice warnings that you have dead code in your applications.

0:12:03.3 VP: So what you take away from the Never enum? Never is a very special type that let's us represent impossible code paths. The Swift compiler is smart enough to understand it and adapt the errors and warningss it'll emit in consequence. And the cool consequence of that also is that when you are writing code and you are only focusing on making your code build, you can use Never as a placeholder for code that you haven't yet written. And it's actually what you'll do for instance, when use fatalError() or preconditionFailure() inside a method. And one last thing, if you like math, you can Google for a critical thing called the Curry-Howard Correspondence, which is the rigorous mathematical framework behind what I've shown you here and the lock and key pattern also that Elliot mentioned this morning. Thank you.

[applause]

0:13:01.1 VP: If you like this and you want more of the same, you can follow me on Twitter and on YouTube.

Edit on GitHub