WWDC2014 Session 404

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Applause ]
>> 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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 an
advanced talk I want to work
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And since this is an
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 parameter --
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 --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 looks like now.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but I want to show you what
a protocol 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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 .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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
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
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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 and 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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 bring
up 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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 dogcow 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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And he carefully
used this property
in his computation
of the window title.
Crusty scratched his head and
thought about his next moved.
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 name 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."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 NSDocumentControllerDelegate,
or it could be an Int,
or it could be a String.
At the call site the compiler
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 thing 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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 we
write correct code the first
time because it doesn't
tolerate this kind of stuff.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ 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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 of 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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 only
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 contact
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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 hid at run time.
And it also steals a lot of
flexibility from the compiler
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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's 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.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
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.