[MUSIC PLAYING]
KEVINE MOORE: Hi, my name is Kevine Moore.
I'm a product manager on the Dart team.
And today with me here is Bob.
BOB NYSTROM: Hi, I'm Bob Nystrom.
I'm a software engineer on the Dart team,
working mostly on the language.
KEVINE MOORE: And we have cool language things to talk
about today, right, Bob?
BOB NYSTROM: So much cool language things to talk about.
KEVINE MOORE: Null safety.
BOB NYSTROM: Yeah, I feel like this is all we talk about.
It's been a long time coming.
But we're very close to having the whole thing fully shipped
and out the door.
And I am very eager to have it done.
KEVINE MOORE: As am I. And this is one of those things
that, you know, you want to do correctly.
So we've taken our time, and we're
making sure we're doing a good job on it.
So maybe before we get into specifics
of how Dart does null safety, can you kind of give the intro
to null safety in general and the idea of null safety
in languages?
BOB NYSTROM: Yeah, so the high level goal with null safety--
and this is something that a number of languages
are doing now is to avoid null reference exceptions at runtime
and instead push those forward into compile time
static checking.
So that's what we're doing.
Part of the reason it's taken us so long to do this on Dart
is that we are taking that approach
and we're pushing it farther than a lot of other languages
in that this system we've built is fully sound.
So the guarantee that we give you is, if you've fully
migrated your code to null safety,
not only will you not statically get any null reference
errors at runtime but, because this is a fully sound system,
the compiler can take advantage of that fact.
So we can use it for optimization purposes.
So we will generate smaller code because we don't
need to insert null checks.
And we'll generate faster code sometimes
because we don't have to box values.
So we're trying to--
we decided that, if we're going to do this,
we're going to try and get as much mileage out of it
as we can, even though it makes it much more difficult,
larger design problem.
KEVINE MOORE: You kind of get it--
I think of it at three levels, right?
So level 1 is I get the red squiggle I expect in IDE.
So it tells me if I'm doing something wrong, which
is great, right?
Just that is great from a kind of productivity perspective.
I can look at an API documentation
and know if null's accepted.
I don't have to guess.
But then we have level 2, which is, at runtime,
assuming all the code has been migrated, the runtimes will
make sure that if null does flow through,
that we get errors upfront.
It will kind of baby sit us at compile time.
And then--
BOB NYSTROM: Yeah.
KEVINE MOORE: --the third level is
we actually get size and speed improvements, potentially.
BOB NYSTROM: Mm-hmm.
KEVINE MOORE: That sounds great.
BOB NYSTROM: The model that we have here is not that--
there's not a moralistic tone, where null is bad
and you should feel bad for using it
and we're going to stamp it out.
It's more that we do feel that null is actually
a pretty useful value, right?
It's nice to be able to represent
the absence of a thing.
That comes into play in a lot of APIs.
So we're not trying to eliminate null.
What we're trying to do is give you tools
so that you can control and understand
where null is going to flow through your program
and, in places where you've allowed
null to flow into it to make sure
that you're using it in a safe way.
KEVINE MOORE: That sounds great.
Let's look at some code.
BOB NYSTROM: I love code.
Code is--
KEVINE MOORE: All right.
BOB NYSTROM: --my favorite part.
KEVINE MOORE: OK, so here's an example here.
And I'm glad you picked a coffee because,
these days, it seems like I drink a lot more of it.
So coffee is a great example.
And it looks like, if you look at the header
here, which says, DartPad with null safety,
so I guess if you're watching this video in a world
before we've actually released this to the wild,
you'll know that there's a special version of DartPad now.
We'll give you details in a bit.
It has null safety enabled.
And what's crazy is this doesn't look
like special Dart code or anything new
beyond the existing Dart.
So this is null safe code?
BOB NYSTROM: Right, yeah, so this is exactly what you
would write before null safety.
And it is also what you would write after null safety.
Our experience is that we've migrated a lot of code
at this point.
About 90% of your code just doesn't need any changes.
So this is the code you would already have.
But now, you're getting fully safe static null checking.
And by that, what we mean is you can
see that, in serveCoffee, you have a parameter of type string
without null safety.
In previous versions of Dart, you
would be allowed to pass null to that.
And that's not a compile error.
But it would fail at runtime when it tried
to call the plus method on it.
Once you've opted into null safety,
that string type annotation does not include null anymore.
That means literally only strings.
But because you are passing a string to it here,
this code is still correct.
And it still does what you want it to do except now you
have the static guarantee that that plus method that you're
calling on temp is never going to throw an exception.
KEVINE MOORE: Cool, so let's throw a wrench in here maybe.
Maybe I'll just make this a--
let's copy this.
And let's do maybe an optional value
because, by default, in Seattle at least,
the default is usually warm coffee.
So I made my parameter here optional.
And I'm getting a warning.
So what's going on there?
BOB NYSTROM: Yes, so this is a good example of where we say,
you know, it's not that we think of null as being bad.
Optimal parameters are a good case
where you can see that having the absence of a value
is a useful thing for an API to express.
But now, you're saying something confusing to the compiler,
right?
So you're saying, I want this parameter to be optional, which
means that, when you don't pass it,
then the value null is going to come in.
But you've given it the type string,
which doesn't permit null.
So the compiler is like, "I don't understand
what you're trying to say here, so you
need to kind of clarify things for me
and tell me what you actually want."
KEVINE MOORE: So the simple thing I'm thinking here
is like, I want hot coffee, right?
BOB NYSTROM: Mm-hmm.
KEVINE MOORE: And so that seems to fix things.
And honestly, what makes me happy
here is I would use named arguments
or positional arguments.
And then there's a paranoid part in the back of my brain
which is like, "I probably should still check for null
even if I past a value in."
But I don't have to do that anymore, right?
BOB NYSTROM: Right, yeah.
Now, null will never come in there.
KEVINE MOORE: Cool, but let's say
we're not going be quite so sophisticated, and actually--
so I need to actually change the type here and say, "Oh, no,
the string might be null."
It might not be provided.
BOB NYSTROM: Yeah, so yeah, so another option
if you don't want to stick in a default value
is you can say like, "Yeah, I actually
do want to allow null to flow in here.
I want to say that null is a valid value for this temp
parameter."
And then in the body of my code, I'll handle that.
KEVINE MOORE: Cool, so it looks like that works.
But obviously, now, we have the problem, which
is this value might be null.
And we're getting an error, which is what you'd expect.
So you'd see this in your IDE as well--
BOB NYSTROM: Right.
KEVINE MOORE: --that I can't add null in a string
together, which makes lots of sense.
BOB NYSTROM: Right, so it's--
by making something nullable, you
were claiming that you will handle it.
But clearly here, we've not handled it.
And the compiler's telling you, you didn't.
KEVINE MOORE: So the naive thing I think I could do,
which is, if this equals null, I guess return "hot coffee".
BOB NYSTROM: Yep, so, dynamically, this
is exactly the kind of code you would write before
and you would write without any static checking
for null safety.
And with a null safety feature, that same code
is exactly what you write now.
And it does what you want.
So there's actually some cool stuff going on under the hood
here, right?
So we do control flow analysis over local variables
and parameters.
So what we can see here is the compiler's smart enough
to know that, if temp is null, then that if
statement in the body that's going to execute.
And it will always return, which means
you'll never get to the last statement
in that serveCoffee function unless temp is not null.
So then it automatically promotes that parameter
to having a non-nullable type.
And then it lets you call methods on it
again because it knows it's statically safe to do that.
KEVINE MOORE: So if comment out this return statement--
BOB NYSTROM: Mm-hmm.
KEVINE MOORE: Boom.
BOB NYSTROM: Right.
KEVINE MOORE: The error comes back
because this might be a null thing.
And I can't add null in a string.
BOB NYSTROM: Yeah, now it's saying
there is a path, at least one path, where that code can
be reached and temp is null.
KEVINE MOORE: So maybe [INAUDIBLE] argument
checking here.
And I could do throw ArgumentError.
BOB NYSTROM: Mm-hmm.
Yeah, and--
KEVINE MOORE: That's works.
BOB NYSTROM: --I think that causes it to be not reachable.
KEVINE MOORE: But obviously, I can't do return null here
because this is not type null.
BOB NYSTROM: Yeah, it's the return type.
KEVINE MOORE: Now, could I even be--
I mean, and even to be, I like having code
with fewer return statements.
I know it's kind of an option.
But could I go in here, just do this?
BOB NYSTROM: Try it.
KEVINE MOORE: Oh, that's super cool.
BOB NYSTROM: Mm-hmm.
KEVINE MOORE: And so that means it's the same thing.
So either an early return means I'm not
going to get here with a null temp,
or if I make sure I assign the variable there, I can do that.
BOB NYSTROM: Yeah, so we actually do
pretty sophisticated control flow analysis and assignment
analysis to make sure that whenever it's statically--
whenever a human would look at this code and be like,
I'm pretty sure I can tell that it's not going to be null,
hopefully the control flow analysis
will be able to come to the same conclusion.
KEVINE MOORE: Cool.
And then I'm also remembering there's something else subtly
we could do here, which is--
BOB NYSTROM: Yep.
Yeah, so the code--
yeah, so this is an interesting example.
So the code we wrote before is if you want to handle null
because you know that null is valid
and that's a thing that you want to accept.
There are cases you run into where you know that something
will not be null.
But it's in a way that you can't prove it
to the static type system.
There are some times where you have dynamic or domain
knowledge about a program that the static type
system can't see.
And you just need to forcibly assert it.
And you just say, look.
I promise you this won't be null.
And the bang operator, of that little postfix exclamation
mark, is one way to do that.
So what that means is it basically says treat--
check that the value is not null.
If it is null, throw an exception,
because we give you a sound system.
We can't just poke a hole in it and let
the code continue to execute.
So there will be a runtime failure if that is null.
But if it's not null, it basically casts it
to its non-nullable type and then lets you do stuff with it.
So that's your sort of explicit opting out
of the static checking for null safety,
and opting into runtime checking,
because you know better the dynamic semantics
of your program.
BOB NYSTROM: Cool.
So in this case, the question mark
kind of helps me define the type,
say this is a [? novel ?] string.
And the exclamation point is effectively
like I'm asserting here this is not null.
Don't give me an error.
So if I actually went in here-- let's do a if temp equals null.
And now I'm not getting an error.
I'm getting a warning which is saying,
this is not needed because we've proven it's not null here.
And so the assertion is not needed, right?
BOB NYSTROM: Yeah.
We've added some kind of productivity warnings
where we give you errors if you are trying to do something
that could be null.
But we also tell you if you have pointless code now.
If we've analyzed it and we can tell it can't be null,
then there's operations that don't make sense for you to do.
And it'll just tell you like hey, you can delete this.
Your code's safe.
You don't need to use the bang operator.
Or if you were to do another if temp equals null check there,
it will tell you that equals null check is also
essentially dead code.
KEVINE MOORE: Sounds good.
BOB NYSTROM: OK, so the previous example
was all within the body of a function
and was working with local variables and parameters.
And control flow analysis works really great for that.
But when it comes to fields, instance field, static fields,
top-level variables, even trying to do
sophisticated whole program analysis,
traditionally doing control flow analysis for that
doesn't work very well.
It's really hard to tell which methods get called
on instances in which order.
So here's a really simple example.
So you have this class.
It has a field.
It has two methods that initialize this field,
and then another method that uses it.
And the intent of the API is that you will always
call either heat or chill before you call serve.
And if you do that, everything works fine.
But the compiler can't prove that you will do that.
So it's giving this error.
And that's what that error's saying.
So it's saying, you have this field.
It hasn't been initialized yet.
And it wants to know what you want to do with that.
So Kevin, what do you want to do with that?
KEVINE MOORE: So I can think of three things.
We'll start with, I think the most obvious,
which is I can just set this up in the constructor.
BOB NYSTROM: Yeah, so that's totally the easiest,
safest fix.
And when you can do that, this is
the best way to do it, right?
So Dart has these constructor initialization lists.
And if you initialize every field there,
then that statically guarantees that you can never
see that field in a non-initialized state.
And the compiler is happy with you.
But sometimes you don't--
when you're creating an instance of a class,
you don't have enough data at that point in time
to fully initialize it, right?
Assume that we designed this weird API for a reason.
And we don't know what temp should
be until after the coffee class has been instantiated.
Then what should we do?
KEVINE MOORE: Well, if you go back to semantics
that we kind of have from before null safety in Dart, which is I
could say, oh, this could be null.
And then I'll assert before I use it that it's not null.
BOB NYSTROM: Right.
Yeah, so this is one solution, right.
You can just acknowledge that yes, this field is unitialized
for part of its time.
And we'll use null to represent that.
So we'll give it a nullable type.
And then at the point that we use it,
we require that it's not null.
So we're going to assert that by using the bang operator.
We know that that will fail at runtime if a user calls serve
without calling heat and chill.
But we're OK with that.
And sometimes, this is a good solution too if you have--
if there was code in the body of this class that was dynamically
checking the temp field to see if it had been initialized
and wanted to be able to see that it was null,
this is a great solution because then it gives you a nice way
to model that.
But in this case, the intent of this class
is you should never read temp and get null out of it, right?
That is not a valid value for this.
So giving that field a nullable type
kind of sends a confusing signal to anyone else maintaining
the class because it looks like null is meaningful.
But really, you just don't want it to be
and you're just trying to shut up the compiler.
KEVINE MOORE: So I think what we need
to do is we have a new keyword, right?
BOB NYSTROM: We do.
So there's another feature we've added for this.
KEVINE MOORE: Late.
BOB NYSTROM: Late.
So late is this new modifier that we've added.
It's kind of like [INAUDIBLE] in Kotlin.
It's kind of like lazy in Swift, kind of covers
both of their use cases.
So as you can see, the compiler shuts up.
It says it's happy with this code
now, which is kind of interesting because the field
isn't being initialized and it can't statically
prove that it's safe.
So what late means is--
one way to think of late is it says,
instead of checking this property statically,
check it at runtime.
So you're saying, I want this field
to have type string, which means it's not nullable.
But by making it late, you're saying,
but enforce that guarantee at runtime.
So what that does is it allows you to initialize it later.
That's kind of why we picked the word late.
But if you read it and it has not been initialized yet,
that will throw a runtime exception.
So it's almost as if every use of temp
in the body of the class gets a sort of automatic bang
operator appended to it, right?
KEVINE MOORE: So this--
I could still get a runtime exception here.
BOB NYSTROM: Mm-hmm.
So that can fail, right?
So the reason we make you write the word late is we
want you to know that you have to opt into this,
because it is something that could cause a runtime failure.
KEVINE MOORE: But I can't go in here and do temp equals null.
BOB NYSTROM: Right.
So that's the cool thing about using late instead of using
string question, is the compiler knows
that you don't want null to be a valid value for this field.
So it won't let you initialize it with null
because you've said no, it's just a string.
KEVINE MOORE: That's super cool.
So maybe we can go through, do another
summarize the language bits we discussed today?
BOB NYSTROM: Yeah, so the way I look at this,
the way we on the language team look at this,
is adding null safety to the language,
first you think of it as just well,
we're just adding static checks to the compiler.
And it's stricter and it yells at you more.
And that gets you part of the way there.
That gets you a language that gives you null safety.
But it doesn't necessarily give you
a language you can use, right?
So we start with that.
And then what we did is we layered on a couple
of extra usability features.
So we have the bang operator.
There's the late modifier.
There's the control flow analysis we do
and the type promotion based on that,
which is a really big part of the usability story.
There are a couple of other features,
too, that we didn't get into, like required named parameters,
probably some other stuff that I'm forgetting.
KEVINE MOORE: Final late, that's pretty cool.
BOB NYSTROM: What's that?
KEVINE MOORE: Final late or late final.
BOB NYSTROM: Yeah, so you can apply the late modifier
to final fields.
And then you get some interesting checking there.
We do definite assignment analysis.
So the intent here, then, if we do our job right,
is we give you the safety and the performance
of a sound non-nullable type system.
But then we also still give you most of the usability
that you had before where you can just
write code that works the way you
expect it to work in an imperative language.
And you just get better static guarantees and performance
while you do that.
KEVINE MOORE: That sounds great.
BOB NYSTROM: Yeah, so that's nice.
The tricky part, though, is that Dart is already in the wild.
And there's millions and millions of lines of code
out there.
So that's the other real big challenge for us.
KEVINE MOORE: Right, so it's important for people
to know that we're working on a model here
where obviously, we have lots of Dart code in the wild.
So how do you get your code to null safety?
So depending on when you're watching this,
we might be in a different spot.
And so we have a website you can go to see
what the current story is.
The first thing we want to let people know
is you can run what we call mixed-mode code, which
is you can have code that has been migrated
to full null safety.
And then in your own package, or packages you depend on,
or packages' code that depends on your package,
they don't have to be migrated yet to null safety.
So we do allow mixed-mode.
There are some trade-offs there, obviously.
You don't get full soundness in that case.
So you don't get all the runtime guarantees that you want.
And obviously, any kind of speed improvement or size
improvement-- and we have some really good size and speed
improvements already--
those won't come until everything is
fully migrated to null safety.
But you can mix and match.
And we have tooling that should make it easier,
both in terms of helping you migrate your code
and also just understanding if your dependencies have been
migrated, you can look on the package site
to see if a package has been migrated.
So we try to make that easy.
And we'll have more information on tools and packages, things
on the package site, later.
As well as, obviously we have a version of Dartpad
you can use now that has null safety enabled
and you can play with.
So all the details will be available whenever
you're watching this at dart.dev/null-safety.
That page will have information about the current state
of things and how you can enable Dart safety in your code.
So for Bob, I'm Kevin, thanks for watching.
BOB NYSTROM: Thanks.
[MUSIC PLAYING]