Tools • iOS, OS X • 1:00:19
Dive deep into Swift! Explore some of its more powerful features, like operator overloading, string interpolation, and advanced pattern matching. Learn how to take advantage of generics to write terse and expressive code.
Speakers: Dave Abrahams, John McCall
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Hi. My name is John McCall. I'm a compiler engineer on the Swift project and a horrible nerd. And today I want to talk to you about how, you know, you can take advantage of really, all of the language tools that we've built into Swift to make your code so much more expressive, and powerful, and safe.
We're going to start off by talking about how you can take control of really the basic language and syntax of Swift. We're going to follow that up by talking about a much more advanced topic of generic programming, and then we're going to finish that up real quick by talking about how Swift is implemented and how it turns your code into great machine code that's finally executed.
So when we designed Swift, we wanted to avoid hard coding too much about the language. We think it's really important to provide a great standard library that lets you really get started right away and making great apps for your users and just jump right in and be able to do what you need to do. But we didn't want to lock you into just the tools that we provided. We wanted you to be able to extend that basic language with new idioms, new abstractions, and not feel like you're locking yourself into that.
In order to show you what I'm trying to get at here, I want to work through an example. And since this is the advanced talk, I want to work through an example that really shows off the advanced capabilities of our platforms. If you've never seen a text adventure before --
[ Applause ]
If you've never seen a text adventure before, the idea is pretty simple. You as the player are wandering around the world solving puzzles and having adventures. You interact with the game by typing in commands at a terminal prompt and the game responds back to you by interpreting those commands, trying to carry them out and then telling you what happens.
So for example in the game you're going to be wandering around, exploring a lot of new locations, and finding a bunch of new objects. And when you find an object you're going to be able to -- you want to be able to look at it, and you know, try to interact with it, and maybe even sort of tear things apart and really get chaotic.
Now, in this scene we've got a whole bunch of different objects that in our game, we're going to have to like manually model one-by-one. It's going to be a lot of text. All these objects are going to be instances ultimately of this Thing class. A Thing is really simple; it's not anything more than just a name and a description and a current location of the object.
Here's a couple of really simple objects that are nothing more than just a name and a description. You can see, you know, we're just calling the initializer directly passing in a couple literal values. We're going to have a ton of objects like this, scenery objects, maybe three or four of these in every single place in the game. Added up over what could be a very large game that means we're going to have several hundreds of these, maybe even thousands. And it's going to be really important to me that this code end up looking really compact. That it be really convenient to read and write.
Well it's already pretty compact but these keyword arguments, these argument names aren't really doing a whole lot for me. I can actually tell straight off what each one of these things are because there's two different strings, there's a name of an object. The two strings, one of them is really short, one of them is really long. It doesn't take much for me to memorize this. It would be really great since I'm going to be writing this over and over again, if I didn't have to have all this redundancy.
So how do we go about actually changing argument names? Well in Swift these argument names come from the declaration of the initializer. By default the parameter names that we use in initializer are also going to be the argument names. But that's just the default, we can expand this syntax out like so. It's exactly equivalent. Now I've got the argument names specifically right next to the parameter name.
But how do we actually make something anonymous? Well Swift has a very consistent syntax for doing this. Instead of giving it a real meaningful identifier, you just give it an underscore. You can use this syntax in a lot of different places in the language. For example, in this small for loop, I'm just iterating all the entries in the dictionary.
But I don't actually care about the values in the dictionary, I just want to print out all the keys. I could give this, you know, its own local variable name, but then if this were a larger loop, you know, somebody coming along and reading it later would instinctively feel -- wonder, if I was actually using the value somewhere in the loop and then feel like they needed to visually search the entire loop. So instead I can just name it with an underscore, which isn't a real variable, it just says ignore this value.
And that's not just in initializations, I can even assign it as a sort of value sync so if I have a color, I can break it down into its color components. But in this case I only care about the red and blue components, not the green and the alpha. So I just assigned the green and alpha components to underscore, which just immediately drops them.
Going back to our initializer, I have all these argument names and if I just removed them I'd end up back in that default state where Swift was using the parameter names as the default argument names. But if I want to drop them completely, I need to tell Swift that I don't want this. And the way I do that is I name it with an underscore. This is just a very simple way of telling Swift I don't want any argument names, just let me call this initializer positionally.
And so I get a much more compact -- I get much more compact definitions of all of my objects. This isn't something you'd necessarily want to do all over the place. There's a lot of value that I get from keyword arguments, from argument names. Usually it provides a really important semantic cue, but in this case I've thought about it very carefully and decided that it's not providing much value and that I'd much rather have the compactness. All right those were a couple simple objects.
In a more complicated object I'm going to - want to be able to give it a -- maybe even additional state, additional logic, maybe make it respond to an action. So for example going back to that original scene I had up, I had some boards that were nailed up in front of the door. I want these boards to be pullable, so I'm going to end up giving it its own subclass of the Thing class.
Well how do I actually make it pullable? My game, when the user types in pull boards, it's going to hand that string off to the parser which will break it down and look -- find out the verb pull, and look around for an object called boards. Let's take over from that part.
We're going to implement a function that takes in a resolved object and implements the pull command on it. Well how do we want that to work at a high level? If the object is pullable, we want to pull it and otherwise we're going to print out some sort of error message saying hey, look you can't pull this thing.
Well that's a great use of a protocol. We've talked about protocols a lot in other talks, but I want to show you what a protocol actually looks like now. You've probably seen this in, you know, reading the entire book, but a protocol really just looks like a sort of blueprint for a type where you aren't actually implementing any of the things in it, you're just describing what requirements are actually there.
A pullable protocol is extremely simple. It just has one method, doesn't take any arguments, doesn't return anything. In order to adapt this protocol, we just go back to our boards class and either extend it or add pullable to the main definition of it. In this case I've decided to add it to the main definition.
The compiler warns me straight off that I haven't actually fully implemented this, so I need to go add the pull method. Which for my boards is going to be -- is very straightforward in its functionality. You just check to see whether the boards are still on the wall, and if they are you move them to the ground and print a message. And then we'll just print an error message if they're not still on the board so that the player knows that they don't need to care about these boards anymore, they're not going to be useful in the rest of the game.
Now let's go back to our perform pull function. How do we actually check whether something is pullable? Well that's very straightforward. We can just use a conditional pass down to the protocol type. This conditional pass will give us a value of a pullable type, which we can then actually pull if it succeeds.
And if it doesn't succeed we're just going to print out an error message. And that's it. That's all we needed to do in order to implement the pull method. Now how do we -- now I want to take a little bit closer of a look at this error message.
I'm going to have a lot of text like this in my game, a lot of messages that need to work -- apply to an arbitrary object. And this isn't bad. This isn't awful syntax, but it's less compact, less natural than it needs to be because of this . "dot" name. Why can't I just put object and string interpolation here? Well I can. Swift knows how to print out an arbitrary object. However, the rules that Swift will use to print out an arbitrary object aren't necessarily the most useful defaults.
How do I actually take over this syntax and hook into string interpolation to actually do what I want? In general the way that you hook into a sort of language feature like this in Swift is that you're going to implement a protocol. A special protocol that the compiler actually already knows about.
For example, I can use special built-in protocols to make my type be usable as a [inaudible] condition. Or to allow the user to iterate over it using a forward loop. Or I could even take over one of the basic literal syntaxes. But in this example what I want is the second one. I want to implement printable so that I can actually -- which is how you take over string interpolation.
Printable is again a very simple protocol. It just has one requirement, which is a property. And all I have to do for that property is implement -- is provide a getter for it. I could also provide a setter, but I don't have to because protocol doesn't require it.
So in this case I'm going to go back and add an extension to thing that implements printable. I have to provide the description protocol and I'll just have it return the name property. And that's it. That's all I needed to do. Well, is this really going to work for an arbitrary object? I mean if I look at this text up here, it doesn't even read right to me as an English speaker, because "a object." That's not grammatical.
When I come along and pull -- and try to pull something that starts with a vowel sound, or that's a plural, or a mass noun in English, you can't just put A in front of it. I need this to be able to work for an arbitrary thing which means I need -- and if I want this to feel natural to the user, so I don't take the player out of their experience, I want this to be actually grammatical.
Well, how do I actually do that? From a class design perspective, I can just define a new property on Thing that's going to be name with article that will throw the right word in front of it, that's great. This is a great class design, but it's not actually very usable for me if I'm going to have a ton of text printing out this thing. This is actually worse than just object .name was. So how can I do better than this? Well I really want to stick something in the string interpolation to sort of modify it.
It would be great if I could just write this -- write an object right there. But that's not actually valid Swift syntax for a lot of very good reasons, but I can sort of slightly tweak this in a way that still feels very natural, that's still very readable, by instead of just putting them next to each other, separating them with a binary operator.
This is a new binary operator that's not currently defined in language. So how do I go about actually adding it? Well the way that you define a new operator for, you know, either taking an existing operator and defining it for a new set of types, or actually adding a new operator entirely, is you're going to need a global function. And the global function just takes for a binary operator it's just going to take two arguments, one for the left hand side, and one for the right hand side. And it's just normal global function with a kind of funky name.
But Swift won't actually let me do this because it's actually checking up on me. Swift doesn't know how to actually parse an expression that uses this as a binary operator. I need to tell Swift the basic rules for this as a binary operator. In order to do that I just need to provide an operator declaration somewhere in my program.
I could throw a lot of information up in here in order to describe how to -- how this operator associates with other binary operators, but in this case I'm not planning on actually using it next to any other binary operators. So all of that is unnecessary. I can just have a very simple declaration.
Now let's go back to this function declaration. The right hand side of this, we want it to be an object. What do we want the left hand side to be? Remember that this is the syntax that we actually want to be able to write. Object here is just an arbitrary expression that's going to be resolved in global scope. And we need -- and it's also going to be an identifier. It needs to actually resolve to something in scope. But we want this to be usable anywhere, so this has to be something that's actually defined at global scope.
And the most natural thing to put at global scope in this case would just be a function. By using a function and having it take the object that was on the right hand side, I can make this syntax very naturally extend to any sort of declarator that I want to stick on the left hand side, I just have to define a new global function with that name. The an function we're just going to implement by using that name with article property that we already defined.
With all of that, I can go back to my operator function and the left hand side of this is now going to be a function value. And the way that we're going to implement this is we're just going to call the function value with the object that we will provide it on the right hand side. And that's it.
That gives me great -- a great new idiom for expressing a sort of decorated interpolated string. This isn't something that you would necessarily want to use casually, but in my game it's valuable enough to me to make this easy to write, that it's worth forcing everybody who's reading my code to learn this new idiom. I want to work through one more example.
I talked a lot about objects in the game, but I haven't talked about places. In the game you're going to be able to walk around, visiting new places. And all those places are going to be connected to other places, by going east, by going north, by going south.
In our class system, place is just a special kind of thing that's going to also have a dictionary that describes all the exits out. When I'm defining objects, this is what that's going to look like as the sort of first pass. But this is kind of syntactically heavyweight. Again, I'm going to have lots of places in my game. I'd like this to look really convenient and natural and more compact than this.
Well, why do I have to write .exits here? Isn't the exits dictionary sort of an implementation detail of my place class? Wouldn't it be more natural if I could just directly subscript into a place and specify where a particular exit goes to? The way that you do that is with a subscript declaration.
A subscript declaration in Swift feels a lot like a property. You use the subscript keyword and then you give it a parameter clause, which is kind of like -- which is going to be all the indexes that are being used to subscript in. And then kind of like a function you give it a return -- what feels like a return type, that's the type of the element that you subscript to.
But here's where it starts feeling more like a property. You just provide a get and a set method like any other property. So the get is just going to delegate to exits. And the set just writes back into exits. All I've done here is provide a subscript directly on place that just immediately delegates down to the exits' property.
But that's going to let me write this in a much more compact and natural style over, and over again. I've been talking a lot about the ways that you can hook into the basic syntax of Swift. I do want to talk a little bit about when you should actually do this.
Taking over a basic syntax like this, developing new idioms, developing things that don't look exactly like other things in Swift can be a little bit dangerous. Not in the sense of being dynamically unsafe, but in the sense of making your code a lot harder to understand. The key thing to doing this well is to make it feel natural. Think about how someone coming along and reading your code later is going to understand it sort of intuitively. Think about what your syntax actually suggests about what it's actually doing.
A major part of that means not taking existing syntax and making it do something that doesn't feel like what other instances of that syntax are doing. For example, my subscript is just sort of providing a view of an aspect of a place. You wouldn't want a subscript to do something completely different.
You could syntactically, of course, the language would let you use subscript operators to do anything you want. You can make it do calls, you can make it take all of your methods and implement them using subscript methods, but that wouldn't be natural. It wouldn't feel like you're really accessing part of the object in a way that a subscript operator does. And that's going to make it a major obstacle to somebody trying to actually understand your code.
And finally it's okay to add new idioms that everybody who reads your code is going to have to learn. People coming into new code bases have to learn new idioms anyway even if they're just expressed using properties and methods, but when you're inventing new idioms using basic syntax, it has a sort of extra cost. It's an extra, you know, thing to learn, that the programmer needs to learn just to understand even what your code looks like.
That's okay to do -- that's okay to expect someone to do if it's actually worthwhile. Make sure that the syntax is paying for itself. That it's worth somebody's time to have actually learned it. And that the value that you're deriving from it actually pays for it. I've been talking a lot about a bunch of very sort of superficial ways that you can extend the language. I'd like to bring up Dave Abrahams to talk about a much deeper and interesting topic, which is generic programming. .
Thanks John.
[ Applause ]
How's everybody doing? Good? Because we were a little concerned. You know. We thought maybe after three days of total immersion in Swift you might be feeling a little uneasy, maybe a little jittery even, because you haven't seen one of these in a while. So I'm just going to give you a moment to sit with it, okay [applause]. Drink it in. Okay. Because you won't see another one before the end of the talk. And we're going to go to our first slide in three, two, one.
Sorry semicolon lovers. Okay so let me tell you a story about my friend, Crusty. Crusty is old school. Programming in Swift is like his one concession to modernity and he doesn't trust modern tools IDEs or debuggers, no he likes to debug his Swift code by logging to the console. He was using a family of logging functions like this.
Now when he needed to look at an interesting string value he would peek String. And if he wanted to peek at an interesting int value, he would peek int and so on. But it turns out every once in a while even old Crusty takes a step into the future. And one day he strode into my office and announced that he had rewritten his logging to use overloading. He said to me, "Dave, well Swift takes a look at the arguments that I'm actually passing and figures out which function I want to call.
Now I just always write peek and leave off the type name." Now I had used overloading before so, you know, this wasn't a big deal for me, but it is kind of a big day when Crusty changes, well anything. So I didn't want to burst his bubble, you know. I was about to say something encouraging when he looked over my shoulder and said, "What in tarnation is that?" And "What," I said. He said, "That." Leaving a smudge on my gorgeous retina display.
[ Applause ]
"Oh Any? Well any is the empty protocol type," I told him. "It's got no operations in it, but it can hold literally anything. And by the time I had looked up Crusty was gone. He had run back to his office to re-implement his logging functions like this. Now this version of peek worked great and it left room for more code in the 80x24 terminal windows that crusty favored [laughter].
Also since everything in Swift can be printed, peek even worked for some types that Crusty had never peeked at before. So all was right with the world and Crusty was happy. That is at least until the doghouse showed up in his window title. Now this was a problem because nothing gets old Crusty's dander up like pesky emoji in his window title. So he said.
See he had written this fancy extension on string adding a computed property so he could eliminate these emoji. And he carefully used this property in his computation of the window title. Crusty scratched his head and thought about his next move. He figured what I really need to do is see if the offending characters are there in the original string. So I'm going to peek at that -- that sub expression.
Now normally he might have rewritten his code like this, so breaking out the expression of interest into a named constant, peeking at that and then carrying on with a computation. But old Crusty had been in a fix like this before. He was sick and tired of reformatting his code every time he wanted to do a little debugging work.
"Ah-ha," thought Crusty, "I know what I'll do. I'll return the interesting value from peek and then I'll be able to insert a call to it right in the expression." Which was an awesome idea, except that doesn't work. So, you see using Any throws away the stringiness of the argument. So of course the compiler doesn't know what's coming out of that peek function. It could be an NS document controller delegate, or it could be an int, or it could be a string. At the call site the complier only sees that signature.
So Crusty knew that he could always downcast to string to get his string back out of the Any. But if there's one thing I can say for Crusty, the man's got taste, you know, he cannot tolerate ugly code and this was starting to get ugly. That's when Crusty dove into the Swift language guide and discovered that the tiniest change could make all the difference. What he did here was turn peek into a generic function. Now a generic function is declared with a type parameter list in angle brackets after the base function name.
In this case, there's just one T. Now you can think of T as a placeholder for any type. And when you write a function this way, Swift deduces what T is from the type of the actual argument you pass. It's a lot like figuring out which function to call in an overload set. In the case above, we passed a string to peek so peek returns a string. Not string wrapped in an any, but good old string.
And it just worked. Now we've seen two very different ways to deal with things of arbitrary type. We could pass them as instances of a protocol type like Any or pullable that John showed you, which erases type information. Or we could pass them as generic parameters, we could let their types bind to the generic parameters of generic functions and that can serve as type information.
Now there's nothing wrong with erasing type information when what you want is dynamic polymorphism. So you want to make a collection of objects that are all different types of objects. And you know that has to be dynamic at runtime. Great. Use type eraser. But when you don't need dynamic polymorphism, there's some pretty compelling upsides to conserving type information.
So first, when the compiler keeps track of what types you're actually using, you don't have to resort to unsafe downcasts like we saw in the previous example. And you don't have to deal with the possibility that those casts might fail. And second, when the compiler knows just what types you're dealing with, it can generate much better code, it can generate code just for those types. And John will be talking more when he comes back about how the internal mechanisms work with that.
Another reason to like conserving type information is that it allows us to express relationships among types. Consider this function, which comes with Swift. It just exchanges two arbitrary values. Here, X and Y can have any type at all, as long as they have the same type. So for example student teacher ratios being what they are, it might make sense to exchange the number of students with the number or teachers, but exchanging the name of your school with the number of students is nonsense.
I love the way Swift helps me write correct code the first time because it doesn't tolerate this kind of stuff. Note that you can't do anything like this with Any or with your similar thing in Objective-C like ID, because converting to these types throws away the type information. It takes real generics, which conserve type information to get this right. Okay let's look at a more interesting example.
Here's a simple function that takes an array of strings, iterates through the indices in the array, looking for an index where there's a string that matches the one that you passed. And if it finds it, it returns the index, otherwise it returns nil. Right? It's returning an optional.
Okay so let's generalize this. First we just find the concrete types that we want to make variable, string and we replace those with generic parameters. But it's not quite that simple. Right, now that T is no longer a known type like string, the compiler isn't entirely sure that we can compare these two things with equal equal. So let's fix that by constraining T to be equatable.
See, the function signature already imposed one type constraint, that the type of value matched the element type of the array. Equatable adds another kind of type constraint for callers to find value. And in exchange for constraining callers, now we have a new capability inside of the body, which is to compare with equal equal. All right let's see how equatable is compared.
Of course equatable is a protocol, a blueprint for type as John put it. Now this one imposes a single requirement that there's an equal equal operator. If you're familiar with protocols in Objective-C, it may help to know that every single Objective-C protocol is also a Swift protocol. However, Swift protocols have some capabilities that Objective-C protocols don't, which makes them especially well-suited to generic program. Case in point, because Swift generics conserve type information, they have access to the full type implementing Any protocol. Here we're saying that there must be an equality operator that takes two instances of the type implementing equatable.
Note that some popular languages implement what they call generics with type eraser, essentially Any plus downcasts. And because these languages throw type information away, they're unable to express even something as fundamental and basic as this in their generic system. But Swift handles it beautifully. All right now let's quickly -- Thank you very much.
[ Applause ]
Now let's quickly make an equatable type. So temperature here is just a little wrapper over and int. And there's a couple of things to notice. First temperature is a struct. Protocol adoption is available to structs, enums, and classes. Not just classes as an Objective-C. Second of all, we've satisfied its operator requirement outside the type body and that's sort of specific to operator requirements. All other requirements you'll find satisfied inside the type body or inside an extension.
Okay. So you may be wondering, well where's not equal. Well it's not a requirement, we didn't have to write it, because Swift provides this one. It's a generic not equal that depends on equatable and uses the equal equal operator to implement it. So that works for every equatable type, which is pretty Swift indeed.
[ Applause ]
Okay let's look at an example of how we can use what we've seen so far. Now I wanted to come up with a really practical example. Something that, you know, you would use in your day to day in your Cocoa programming. So what we're going to be doing is computing phi the Golden Mean, which is the ratio of consecutive Fibonacci numbers as N approaches infinity. The slide warned you that we were horrible nerds right? Okay. So first we need to computer the N Fibonacci number.
So that's just the sum of the previous two Fibonacci numbers where the first two Fibonacci numbers are zero and 1. Okay, this is not the fastest way to compute the Fibonacci numbers, but it is really hard to beat for its simplicity, and mathematical purity, and elegance. It's really easy to verify that this does exactly the right thing.
And here's phi. We need to go a few iterations right up to about 45 so that we get enough precision with our estimate. Now running this part of our program takes 11 seconds, 11 seconds on a fast machine. Well it's easy to see why if you look at the call graph. So just looking at Fibonacci at 5, that depends on Fibonacci at 4 and 3.
And Fibonacci of 4 depends on Fibonacci of 3 and 2. And you can already start to see that it repeated computations in here. And if you look at the entire call graph, well you can see there's a lot of repetition. Now expand this up 45 levels and you've got a recipe for a slow program.
However, if we could just store our results in a dictionary the first time we compute them, then we could turn all of these calls into fast lookups, right. And these calls, well they would just disappear entirely. This technique is called memoization. And while Fibonacci makes a great example for it because it -- you know, recalls the same function over, and over. You can apply it to speed up any pure function where you might be calling it over and over with the same sets of arguments.
Okay let's manually memoize Fibonacci, okay. First we need a dictionary. Next we change the function body so that it checks the dictionary to see if it's got the result and only computes the result if it isn't found there. Now how does our function perform? Okay 100x speedup, that's pretty good.
It's 100 times more Swift, okay [applause]. Please forgive me, please forgive me. Now this is awesome, but we've kind of destroyed the readability and mathematical purity of our function. I mean, if you look in there really hard you might be able to find the original computation among all of that boilerplate. There it is.
It would be nice if we could encapsulate all of that road code transformation so we could easily memoize any function without destroying its readability. Something like this. Well in Swift, you can. In fact memoize isn't in the language, it's just a generic function I wrote. And the code between the curlies, well that's a closure or an anonymous function being passed as an argument to memoize with trailing closure syntax. Memoize returns another closure and that's what we're storing in Fibonacci. So Fibonacci's just like a function. You know a regular function is just a constant bound to a closure.
So in fact you don't even need that type annotation in this case because Swift type inference can figure it all out for you. And now memoize is general. So when I discover that my app is bleeding CPU cycles by parsing the same property list strings over and over again I can go back and use memoize again, just like this.
Okay let me show you how this works. So this is a first cut at a memoization function. A simple version. All right, yes I know it doesn't look that simple, but I'll take it apart for you. So it takes one parameter called body which is the closure, right? And the types, the argument and return types of that closure are arbitrary except that there's this constraint on the argument type that it be hashable. Why do we need that? Well so we can use the argument type as a key in the dictionary. Right? And it returns the same type of closure that it gets.
Okay. Inside the body this is actually pretty straightforward. So first we create the dictionary that we're going to need to use to memoize that function. And then we return a closure which is wrapped around an invocation of the body which is the actual computation, right, and the usual memoization dance. Where we look in the dictionary and return the value we found if we found it, otherwise compute and put it in the dictionary.
Okay. Now this version of memoize works great for functions like parse property list. This just works, which is awesome, but for recursive functions, like factorial, or Fibonacci, well not so much. You see Swift doesn't want us to use a variable's own value to initialize it. Like initialize itself in terms of itself. That just doesn't make sense, right, it's usually a terrible programming error.
So what can we do to get out of this? Well we could do this two-phase initialization dance. Here what we've done is we've made factorial a variable. And we've initialized it with something throwaway like the identity function, right? Dollar zero in braces just returns the argument it gets. And then we reassign factorial to memoize.
Okay this works, but it's got a few downsides. First of all, it's ugly so Crusty's not going to be happy with us, right. Second of all, we had to write out the explicit type annotation, that Int arrow Int. And that used to be deduced for us. But most importantly of all, it makes factorial mutable, which we didn't intend.
And keeping things immutable as often as possible is a, you know, is a great path toward correctness and easy to reason about programs and all kinds of things, including thread safety. So fortunately, there's a better way. Ready for take two? I'm going to warn you in advance that this is a little bit mind blowing, okay? If your mind's not already blown. So let's just have memoize pass factorial as an argument to its own body.
Right? Okay but stick with me you'll get it, okay. So if we can pass factorial in it as an argument to this body, then that factorial on the right, well that refers just back to the function parameter. It's just like referring to X. See now our closure has two parameters, a function and X.
All right so what do we need to do to make this happen? Well first we need an additional parameter to body right. You can see body is taking now a new parameter. And that parameter has the same type as we're returning for memoize. Whoa lots of arrow. Everybody okay? Okay. I'm sorry. Next -- it will get easy again. Next, we do the two-phase initialization dance.
So here we can't really get out of the two-phase initialization problem, but at least we can hide it inside of memoize. And two-phase initialization scenarios like this one are a great application for implicitly unwrapped optionals. Because right, once you've initialized the thing, after that the thing can never be nil. So there's no point in going through all that syntactic baggage of unwrapping it. And in this case, the implicitly unwrapped optional unwraps when we return it as a non-optional, right.
And because it dies at the end of this scope, well any scary possibilities of it being nil die along with it. So this is actually pretty elegant. Now all that remains is to pass result to the body when it's invoked and there you have it. A reusable tool that elegantly memoizes even recursive functions.
[ Applause ]
Now the point of course is not that you're going to go out and memoize all your functions, but that you can do stuff like this in Swift. You can write your own modifying, crazy language extension-type functions like this. And it's pretty cool. So being able to do this relied on the synergy of three powerful features. First, type deduction for concision, so we would have compact and readable code. Next, trailing closure syntax, which evokes control flow while supporting functional programming idioms. And lastly, truly generic functions that are flexible, safe, and fast.
Okay. Now I want to bring it back down and talk a little bit about generic types. So you've already seen a bunch of generic types yourself. Arrays in Swift are just generic structs and so are our dictionaries. And optionals are just generic enums. And if you've watched the other presentations, I know that was covered. You can also make generic classes in Swift. Let's make a generic struct.
So we'll start with a simple concrete struct, concrete stack of strings. It's got push and pop methods and it's just implemented in terms of an array. Now let's make this into a stack of any type. We just do what we did with our generic functions. When we made a concrete function generic, we took the concrete types and replaced them with a type parameter, and there you have it. Now I can make a stack of Ints or a stack of NSWindows if I like. So notice that unlike with generic functions, when you use a generic type you actually supply the type arguments explicitly most of the time. With functions the type arguments are always deduced.
Now Crusty probably wants to be able to log our stack. And he would probably write his logging function like this. But unfortunately that's not going to work. It's not going to work because the for Int syntax as John mentioned is governed by this sequence protocol and we haven't implemented it. So let's take a look under the hood at how Swift does for...in loops. So when you write a loop like this, Swift internally rewrites your code like this. So what's happening here? First it goes to your sequence and it calls generate, to get a generator out.
Next, it repeatedly calls the next function on your generator until it gets nil. So next returns an optional. And those optionals are filled in with values until the sequence runs out. Okay so what is this generator thing? I'm sure you can guess. It's a protocol, okay. And the first thing you see in this protocol is this type alias. Now when you see a type alias in a protocol, that's called an associated type requirement.
Okay, it can be satisfied just by writing any nested type called element inside your generator, but why do we do this? Well it's usually a type that's involved in one of the protocol's other requirements, in this case, Next. You have to express that, you know, here's a name for a type that's going to come out of Next.
So let's build a generator for stack. Here's the code. So it's just another generic struct, right. It conforms to generator. And you can see that it implements all the necessary parts of the blueprint here. Now we've written a little bit more here than we actually had to. See when the compiler matches up that next requirement with this Next function, it can see that the element type has to be T. So that associated type is deduced and we can just leave it out.
That gets really convenient. The next thing I need to point out is that we've used this Slice type. So Slice is a lot like array, in fact you make them from arrays by slicing the array using this syntax. So you pass a range to the subscript operator. And Slice differs from array in that you can efficiently drop things off the front of the Slice in order one time. So that's why we're using it because we want to go forward through this sequence.
So, what do we do? First we check to see if the Slice is empty, if it is we return nil, otherwise we get the first item off the Slice. Replace the Slice with the rest of the elements and return. And there's our complete generator. But we're not done yet because we haven't implemented sequence.
Right, this is a two-protocol, protocol. So sequence has a very similar structure to that of generator. The most notable difference is that its associated type is constrained here to be a generator and that's how Swift knows that it can call next on the thing that gets out of generate.
Okay, so let's implement sequence for our stack. Here it is. Notice first that I've done the entire implementation inside an extension that's dedicated to that protocol. And this is a really slick way to partition your code especially if you have a lot of protocol conformances. And that's going to be pretty common in Swift. In a lot of ways Swift is a protocol-based language. Next, I want you to notice that I haven't given the associated type explicitly, right. It gets deduced from the signature of this function, which returns a stack generator, which is the thing we just created.
Lastly, I want to point out there are some circumstances where you don't even need to write the type parameters on a generic type. In this case, I'm returning stack generator and I haven't written the T in angled brackets. That's because the type context, the fact that we're returning the stack generator of T, allows the compiler to deduce what type that is.
Okay and now we can finally loop over the elements of our stack and Crusty is happy again. Okay, there is a lot more I could tell you about generics and Swift. And I know this was pretty intense. So we're going to stop here. I wish we had time to cover the collection protocols, the index protocol, protocol refinement.
How to build lazy functional adapters like we have, like map, filter, and reverse like we have in the standard library, but you know, you can find all of that stuff if you dig into the documentation. If you remember only three things about this part of the talk, let it be these. First, protocols are what let you hook into the basic core language features like for Int, and string interpolation.
Second, generics offer a new dimension of speed, expressivity, and safety for people coming Objective-C. You can do really totally new things. And lastly, Swift is fun. I encourage you to dig into these capabilities and find out as much as you can. Experiment. Play around. Okay. Now I'm going to bring up John to close the talk. He's going to tell you a bit about the Swift model.
[ Applause ]
We talked a lot about what you can do in Swift and I want to tie it up by talking about how Swift actually works in a couple of quick ways. Like C, Objective-C, and C++, Swift is a statically compiled language with relatively small runtime requirements. That's not a coincidence and it's not because we were forced into it in anyway. We actually believe very, very strongly in this model of programming languages as a great model for you.
It's really flexible. It's really predictable. And it's really efficient. It's flexible because it allows really simple interoperation. You don't have to write everything in Swift. Our runtime requirements are so small that we can just transparently interact with your existing C, Objective-C, or Assembly -- Ada, so on. Don't write your code Ada [laughter].
And all of that makes it really straightforward to deploy Swift to versions of iOS and OS X that don't even know anything about the language. That were developed without Swift in mind. Swift is a really predictable model. Because it leaves you fully in charge of the code that's actually going to run on your users' devices.
The compiler's going to weave a lot of complicated magic, making generics work, optimizing this and that and so on. But when all of that is done, when all of it settles, you see exactly what's left and you can feel confident that -- that in how that's actually going to run on a device.
There are no extra, just in time compilation steps where all the really interesting optimizations are implemented. There's no non-deterministic places where like a secondary thread is pausing your entire application to garbage-collect right in the middle of a user operation. You can understand your code exactly how it was compiled, and exactly the result and feel confident in exactly how it's going to run.
And finally, it's really efficient. Swift generates native code. Native code that's ready to run as soon as you put in on a device. There are no recompilation or warm-up delays while your app launches. You're free to organize all of your high-level code in Swift into clean and easy to maintain abstractions using powerful things like generics.
And because it's all statically compiled that abstraction disappears immediately during compilation not later when the jet actually kicks in and lowers it all down to nothing, hopefully. And the predictability of compilation means that you can really feel confident in exactly what is going to run after all this is done. So you can feel confident that you're really tight, efficient, low-level code will always do exactly what you expect.
I want to talk a little bit about the Swift compiler architecture. The way that we accomplish this is very, very similar to how these [inaudible] C compilers are structured with one major modification. We add an extra step, an extra phase of compilation for high-level analysis and optimization. These are language-specific analyses. Things that we know specially about Swift and its library that allows us to do very high-level manipulations and produce really great code straight off. I want to talk about three of those in particular.
The first one is I want to talk about abstraction penalties. Suppose that you're writing an application, and it's got -- and it's talking to a whole bunch of sensors and a whole bunch of different subsystems and some of them are giving you values, you know, values back in one kind of unit and some of them are giving you values back in a different kind of unit. And it's really important to you that you're not app -- not burn up when it re-enters the Martian atmosphere.
You can use the type system in Swift to do this. Structs have zero added run time extraction costs, which we've designed Swift from ground-up to eliminate this kind of abstraction cost transparently. In fact, in Swift even basic fundamental library types like Int and Float are actually implemented as Swift as struct types that are wrapping even more fundamental LLVM types. So you can feel very, very confident that we've done an extraordinary amount of work to make sure that these things don't add any extra overhead.
The second thing I want to talk about is generic specialization. Some languages implement generics by immediately expanding out your code whenever you use it with a different set of generic arguments. Now that generates very, very fast code for this particular expansion because it means that like the code generator never even sees the concept of a generic function.
But unfortunately, there are a couple downsides to this. It's terrible for debug build times and it ends up generating a ton of code that the compiler and the linker and everything else need to conspire together to try to hide at run time. And it also steals a lot of flexibility from the compiler to actually unify these things. So in Swift, generic specialization is an optimization. It's something that we can do, but we also maintain the ability to run generic code as generic code.
The last thing I want to talk about is de-virtualization. De-virtualization is an incredibly important optimization in Swift, because so much of your code is written around classes. It's very important for us to be able to take something very simple and very lightweight like a getter and turn that into direct manipulation of memory.
There are a lot of ways that we can do de-virtualization in Swift. We can see where you're actually constructing the object. We can do hierarchy analysis to see that a class doesn't have any sub-classes. But you can also take control of this manually by marking methods and classes as final, in order to tell Swift that it doesn't have to worry about the possibility of it being overridden anywhere. There are a lot of other high-level optimizations that I really wish that I had the time to talk to, but I'm actually already two minutes over time.