- Well, it's kind of, it's funny that it
was unexpected which segues into the title of this talk.
So, what happened was I was in St. Petersburg
at a conference and I was walking with the organizers
back from the restaurant; it was like 12 AM
in St. Petersburg.
And we walked down the street at 12 AM
and, you know, I naturally ask,
"well, is this safe? Are we safe here? What's going on?"
And he's "ah, yeah, it's safe. It's good.
"It's totally safe, totally fine."
Like, "Well, how about all those videos I see,
"you know, with, you know, the guys who
"throw axes at each other?"
"Ah, never happened."
"How about the woman that carries a drill on her back?"
"Ah, never happened."
"How about the train thing that,
"thing that crosses the street and stuff?"
Like, "Ah, it's just like, maybe once."
Now I'm okay. Cool.
So, okay, this is not a joke. It's a story.
It's a true story.
So, three minutes later.
So we walk down the street and three minutes later
there's a fender bender, like maybe, I don't know,
20 meters from, from us.
Sorry, this is America.
66 feet. (crowd laughs)
66 feet from us and a half.
So it's like, there's a fender bender
and we see in real time, four guys jumping out of one car.
And three guys jumping from the other car.
And without any introductions,
they start punching each other in the face.
This is God's honest truth.
They start punching each other in the face, like.
But, you know, that's funny.
There was no, it was like choreography.
Cause they like jump out of the cars
in like, in a ballet motion, you know,
they start, you know, they start hitting each other.
Like, how about like, "Come get a piece of me."
All that trash talk. There's no trash talk, right?
So, they just started.
It was like on a cue they started punching
each other in the face.
So now, it turned out
by some happenstance, it turns out
I was the tallest and the biggest guy in there.
You know, there are like two more guys and two women.
So, it so happens that I was the tallest,
the biggest guy; so the women hid behind me.
And what I said was, "don't worry, you're with me."
What went on in my mind at the time was,
"Oh my god! They're punching each other in the face!"
And what I should have said was,
"Young lady, you should know I've never
"thrown a punch in my life.
"So if you ever threw a punch at your brother
"when you were five, you're more qualified than me."
(crowd laughs) "And by the way,
the 20 pounds I have over these
two other guys, that's not muscle."
So, anyhow. So, I do recommend visiting St. Petersburg
in spite of the story, I think it's a great city.
And it's actually, you think,
"okay, what? So it's a city, right?"
So, it's a nice city somewhere in Europe.
You go there, you know, 30 minutes later.
So I got this guide and 30 minutes later
I was like, "Oh my god! This is awesome!
"So much good stuff here!"
Which brings us to Expected.
Alright, so, today we're gonna do a little experiment.
Which would be reinventing exceptions.
And this project started--
actually, let me, let's actually discuss
exceptions a bit.
As a means to handle errors and then
we're going to get into expected.
So, most of us, I remember when I learned about
trying catch, it was the days when
the syntax coloring for the first time in editors.
So, like, you type trying it becomes blue.
That was amazing, remember?
Like, "oh my god, it's blue! This is awesome!"
"I'm gonna try and catch in every single function I write!"
So I was very happy with using Trying Catch.
I didn't think about it critically, right?
So, then there's all of that,
you know, all of the ordeals that the C++
committee went through
to discover it's way around exceptions.
So, most of us don't do much in criticals
so here's, you know, Trying Catch,
and you just use it.
Have a good day and everything.
But, you know, we don't know what,
you know, what's, what are their goals
and how do they compare against
Ultimate of Missover handling.
What were their intended use cases?
How does their semantics support
what we want to get done?
And what are the consequences of the new set up
of the world?
So, most importantly, how do we write code
that is a nice exception, you know,
compliant if you wish?
Well, there are so many means to choose from.
Well, you know, there's a new one too.
Herb Sander's proposal?
Alright, raise your hand, I'm not seeing anything
but raise your hand, please, if you've heard of it.
Okay, I see nothing, but, you know, thank you.
You should, if this was, like,
30 years ago and everybody smoked,
you'd be, like, you know, raise your highlight,
you know, your lighter, there, and.
Anyhow, so, essentially, what's funny about
exceptions and error handling in general
is that, it's the, the meaning of
what's exceptional and what's not.
Depends a lot on the context.
The same issue may be a completely expected matter
in one set up and something that is completely
not allowed in another context.
So, we want to learn once and use many.
We want to learn one mechanism and use it many times
for error handling in general.
We want to minimize what's called soft errors
and maximize hard errors.
So, soft error would be an error that
doesn't get detected immediately.
So it's a, what would be an example
of a soft error? Please shout.
(man shouts inaudibly)
I didn't hear that but I assume
that he has said dangling pointer.
Did I? Was I?
Not even close? Okay.
(crowd laughs) File not found.
Okay, so we have a dangling pointer error
and it's gonna, sometimes the application
keeps on trudging along and it kind of almost works.
Right? So that would be a soft error.
And a hard error is a, it's detected immediately upon,
upon it happening.
So, that's what we're looking for, right?
We don't want those metastable states in which,
essentially all of the guarantees provided
by the system are gone.
We don't want that.
And then, essentially we have no guarantees.
So it's kind of the application is like,
really like on skits there, right? Not nice.
Well, we also want to do things like
centralized handling but also local handling.
We want everything and we want to pay nothing for it.
This is C++, c'mon. Right?
We want to try whenever I want local,
I want it to be nice but, you know,
when I don't care about local I want the centralized
to be nice and you go design that.
And, by the way, I want it cheap.
So, these are issues, right?
These are kind of difficult,
difficult design issues for our program language.
We want to be able to transport from
wherever the error happened to wherever
the error is handled however much information we want.
For example if it's an out of memory error,
we want to transport four gigabytes of memory.
(crowd laughs) Yeah, thank you.
That was a joke, yes.
Thanks for laughing, yes. Thank you very much.
So, we want little cost and a normal path.
There's gonna be more about this later.
And we want to make correct code easy to write
and incorrect code difficult to write.
So, starting with a simple example,
which is actually, will live in infamy; atoi.
If you google for atoi, I think the first hit
is like, "avoid atoi."
Why? Because it returns an int.
If it returns, if atoi encounters any error,
btoi doesn't look like an integer.
Or maybe it's too big of an integer and doesn't fit.
It's going to return the most unlikely integer ever.
Zero. (crowd laughs)
So, then it returns zero and they're like,
"uh-huh, this could be a legit zero.
"Or it could be an error; so I gotta check."
So then what you do is, maybe use a regex.
Which is like, you know, just like
one or 56 times more complicated than atoi.
So, you're better off just re-implementing
the god damn atoi yourself. (crowd laughs)
And the problem with atoi is illustrative
because atoi covers all of the values of an int.
It's a surjection, they call it.
So every int is covered by atoi.
There is some, there's a string that
represents every int; there's no "null int."
Right? There's no int that doesn't exist.
With flow, there is a flow that doesn't exist.
It's called "not a number" right?
But with int we don't have that luxury.
So, not nice.
So what do we do about this kind of stuff, right?
Well, there's always errno.
Which, by the way, atoi is not guaranteed to set.
Did you know that?
I read it.
So I gotta be prepared for this stuff
so I can make fun of everybody right?
So, with errno, it's a whenever something goes wrong
it's eternal and pray that somebody's gonna check it
at some point in the lifetime of the application.
It's going to be fairly general,
it's going to minimize soft errors, no.
Because where was the last time it checked
the result of print f?
It could fail! I mean, come on, right?
Invalid file handling and whatever, right?
But nobody checks print f.
So, we want to have centralized error handling,
which errno supports nicely because
it's centralized and we want to support
local handling, which, it kind of supports.
But we can't transport an arbitrary amount
of error, it's just an integer.
And, essentially, you know, the worst of errno,
which is, you gotta make sure you allocate
the values in the integer appropriately
so it don't step over over the operating system's
reserved values and all that nonsense, right?
And all the headers and all the stuff,
it's in an industry there.
You know, I sell errno values for cheap!
So, not nice.
And, you know, errno is not famous
for making correct code easy to write.
You know, so it's not the best choice.
So, there's a lot of red on that slide is all I'm saying.
Well, there would be a special value scenario
which is coming along in this nice,
oh, I have this fancy thing here.
This nice strtol long function;
which takes a pointer to character
and a pointer to, a pointer trick character.
Which is going to be filled with wherever
the conversion stopped.
So, it's filling the, it's giving information
about where the conversion stopped.
So then you gotta kind of track back
and try to do things and, you know,
figure out what the hell happened and that kind of stuff.
It's kind of difficult to distinguish things
like there was an overflow versus it wasn't a number.
So, it's kind of not nice.
And r would be the radix.
So, not very cool.
So, it's not very general, it's not very nice.
Let me point out, let me kind of insist a bit
on the returning especially in here.
Like, we have an atoi; it's returning
a special value which is zero.
The special value thing would
work if you return pointers.
Because there is one pointer that
is unlike any other pointer.
And that would be? (crowd responds)
Thank you very much, the null pointer.
So there's a singular pointer
that doesn't point to any value, et cetera, et cetera.
So it could actually, at the minimum,
say well there's a single one,
there's a singular value of the point I'm on.
We can't do that.
But for most other values you can't.
If it's a date, you can't really return.
Actually, you could if you really wanted.
But it's not general, there's no generality there, right?
So, it's not gonna minimize soft errors
and it's not gonna do good centralized handling.
It's only good for local handling.
So, not very nice.
So, we have this unpleasant situation, too.
So, now, let's actually design together,
a system that would work.
And I'm thinking, you know,
thinking naively, or just, you know,
again, like, clean the slate.
You know, clear up your mind.
Take a yoga pose right now, okay?
So, just think of, "how would I design atoi
"if I had, like, complete freedom
"and I could design my own language,
"invent keywords, and whatnot?"
So all those sound like, "well,
"there must be some type that
"represents there's an invalid input going on."
And then I'd say, "well, atoi returns."
There's an order, you see?
It's some sort of an option, you know,
it's kind of, well, it's either an int
or it's an invalid input.
But, like, this is not C++, right?
I mean, I don't need to tell you.
It's kind of, I have a dream.
So then, we have, we invent a keyword because we can.
Typeswitch and depending on the result of atoi,
we're going to say, "well, if it was an int,
"I'm going to handle the happy case.
"And if it was an invalid input error,
"I'm going to handle the unhappy case."
And some languages do quite that, actually.
So, that's a nice thing.
Well, there's something; there is a rub here, however.
These two types are not, um, symmetric.
They don't, you know, here looks like,
well it's an int or invalid input
and it looks like the branches have equal weight.
See what I'm saying?
Rhetorical question, I know, because, like,
again, I can't see nothing, right?
So, um, rhetorical, you see what I'm saying.
So, it's either an int or this guy
but they have, sort of, equal roles
except the int comes first.
But, what I'm looking for is some sort of
I'm expecting it so it's a probability thing, almost.
Well, I expect an int.
Or I don't expect a punch in the face, right?
I don't there, it's something rare.
Statistically speaking and, you know,
kind of, correctness, whatever speaking.
So that would be, that would convey
the fact that these two, these two types
have an ability and a symmetry as far
as the function atoi is concerned.
Thank you, thank you, I hear, I heard a yes.
So, local code should just simply
just blow through it and go with a happy case.
Just, you know, I'm gonna just go happy case
and if anything bad happens, you know,
u-type system, please take care of me.
That would be the socialist-type system, as it were.
It takes care of you if you do something wrong,
I told you I'm gonna do a political joke.
So, now we have a function that has,
like, an over-return type and one or more
covert return types because many things could go wrong.
But only one thing could go right, as it were, right?
So the question is, okay, well how about those
unexpected things; where do they go?
you know when you call main there's gonna
be function call and there's a stack there
and very nice and you gotta return out the stack
because there's no other point you can return.
Cause that's the execution, you know,
that's the trace you got.
You can't go to, kind of, in a new place.
That's not on the call-stack.
So, it's gotta be on the call-stack.
You see, I feel like a con-man here
because I'm gonna con you into, like,
we're gonna go, well, in we invented exceptions.
There's no, you know, there's no output.
There's no chance we don't do that.
But let me, let me con you.
So what, there's gotta be a return for the,
these unexpected things.
And certainly, there must be some,
you know, some handles placed somewhere
in the invocation stack that are going
to take care of this unexpected things.
Thank you for the yes, and thank you for the yes.
I heard no "no."
So now, the callers mark must plan
some handling spots in the execution path
and, you know, the covert returns,
kind of, jump automatically to those handling spots.
And guess what we just invented?
Thundering applause right now but,
you know, let's keep that
because that was happening, like,
30 years ago, right?
We just, you know, by essentials,
we didn't invent much as acknowledge situations.
And the situation leads you to this thing.
Well, you gotta put some handles on
the call-stack because the call-stack
is all you've got in terms of flow, right?
I mean, you gotta go somewhere on the call-stack.
And, therefore, you have all these tries
and catches that plant, well, exception handlers.
So, well, let's see how exceptions go
as far as respecting our desires.
Well, they have, there's a lot of blue.
There's a lot of blue.
They're pretty general.
Well, there's a question mark there
we're gonna discuss a bit more though.
But they do centralized handling awesomely.
You put in main, you put the try,
you put the catch, you catch everybody sees right there.
You're good. Right?
Don't forget to catch my reference.
So, then you have a, you know,
you have an arbitrary amount of error information;
you can put anything in exception if you wish.
No problem at all.
There's little cost on the normal path,
we can talk about that more.
But they don't do local handling very well.
Like, if I want, like, call a function
and get the error or whatever,
I gotta write, like, five lines of code,
which is ridiculous.
That's kind of the poor case, the poor scenario
in which, "well, I gotta try this right now
"and do a try and catch."
It's a very heavy syntax for achieving a simple thing.
And, as far as making correct code, you know,
easy to write, it depends quite literally
on the year you're asking.
Alright, so, you know, skipping back to '98;
yay, we got exceptions and they are blue too!
That's amazing, that syntax color!
Try Catch, all that stuff, throw!
It's all blue!
But, you know, there's the Tom Cargill article,
I'm sure, those of you who can, you know,
who have the age, can remember it.
And there's, like, a lot of churn
about what's going on with exceptions
and there's like, you know, difficulties
with the simplest transactional codes.
So, 2008 was kind of the Dark Age of exceptions,
if you wish, in C++.
I think that's where the market failed.
I think there must have been some causation there.
And nowadays there's a big maybe.
Because, you know, as a Charley Bey,
a famous Boost contributor, mentioned in a report
on Boost there, he said, "error handling
"idioms and practices do remain contentious
"and confusing within the community."
There's only one way that everybody likes and recommends.
So, well, today we're gonna, we're gonna introduce
one more means of handling errors.
As if there are not enough.
You know, the joke with the standards?
I like about standards that there
are so many to choose from.
Same way about error handling in C++.
Now we have, like, error codes,
we have errno, we have exceptions,
we have Herb's proposal, which is pretty much, uh;
kind of, it's a special value thread
that through automatically.
And, you in the room that's ready
to change history, right now, friends?
(crowd laughs) So, this is what's
gonna happen right now.
So, let's recap what the issues we have with exceptions.
We have this whole Metastable states,
which is, I like fancy words, and this is,
this is my sin here in saying Metastable.
All that stuff is like an application
that went off the rails, my friends.
It's just, "oh my god, no body knows
"what happens next."
As I'm sure you know, user must ensure
the transactional semantics by hand.
And we have, you know, we have destructors,
we have ScopeGuard as possible solutions.
We have the whole local error handling issue,
which is unpleasant and inefficient.
By the way, plenty like, make a
quick parenthesis right now.
You should know that just enable exceptions
in your project is going to make it slower.
Even if you don't use exceptions.
Thank you for the nod, Andrei.
No, there's a guy Andrei, it's not,
There's actually a person called Andrei
who's nodding yes.
I do see the first row, I gotta confess.
The thing is, with exceptions enabled,
the compiler must generate the entry
and the exit sequence for each function
a bit differently and more, you know, bulky.
It's more bulky.
So, again, we've seen at Facebook,
I remember we've seen things like, well, seven percent.
And seven percent could be, like,
quite a lot of power.
So, not nice.
And people have measured.
Like, it could be, like, two percent,
could be, like, unmeasurable.
But it could be, you know, quite a few percent.
So, you know, when they say zero overhead
That's not really zero.
It's a floating point number.
It's just kind of, you know, a bit more than zero.
So, anyway, parenthesis closed.
So now, you know, the whole exceptions
and Herb Sander has this famous examples
with like, a five line function
that could go through, like, five million
exceptions, you know, from five million
different paths and all that good stuff.
So, it's kind of a difficult proposition right now.
And the, the least nice of all
is that composition with exceptions is tenuous.
It's really, like, bad to do.
Well, I have two exceptions and I want
to collect them in a vector of exceptions and you can't.
All you are, one exception can be active at any moment.
But you know I'm lying, right?
Because more than one exception can be in flight
if you throw within a catch handler.
Because the exception is not handled unless,
you know, until the catch is done.
So if you have a catch and inside
you call a function and it has another
exception and stuff, there are several exceptions active.
And that led to things like, you know,
as opposed to the useless on-code exception.
Raise your hand.
Alright, alright, thank you.
So, you know, that leads us to, welcome to my talk.
We are just starting right now.
So, we're going to do with exceptions,
we're going to do local handling,
we're going to do minimize soft errors,
and we're going to do improvements
on making correct codes easier to write.
But we must start with a few background items.
Background item number one, is my Wikipedia page.
Yes, I have a Wikipedia page.
And I have more United miles than you have.
But I don't fly United anymore, by the way,
it's just that they, they punch in the face, right?
I don't want that.
So, I quit.
So, I was visiting my Wikipedia page
which I never contribute to,
I just let other people, kind of, destroy me.
So, I was watching as, there's this change at some point.
I really, I literally visit every year,
a couple of times.
I don't follow.
But anyway, at some point, there was somebody
who deleted a lot of stuff.
And what they did was they added something.
And it was Andrei is accredited with
creating the expected type for C++
which is being standardized.
And I was like, "huh, I should check that out!"
Honest to God, I had no idea.
So what really happened was, like,
back in the day, a few years ago,
I had this idea, which I gave a talk about that.
C++ and beyond seminar.
And a couple of guys thought,
"oh, that's pretty cool so let's standardize that."
So, the rest, as they say, is history.
The second background piece (clears throat)
would be std variant.
Or boost variant which is
the perfect implementation engine for,
remember, we had the overt and the covert type.
We have the expected and the unexpected.
It seems like they could live in a variant.
It looks like this variant is the perfect
place to store such a thing which
can be one of two things but never simultaneously.
So now, we have, kind of something that's really
close to what we need which is std optional.
Which is either something or nothing.
But we don't want something or nothing,
we want something or something else;
which would be the exception.
So, it's closed but just, you know,
related but not really it.
And we can go to other languages
and does the maybe/ either monads
which also are nearly there but not quite yet.
Not quite there, right?
So, everything of this, all of this is
for us to anchor in our mind the devices
that we're going to use for implementing this.
There's one more; which is also,
like, a funny, kind of very related.
Promise and future in C++, yes?
Promise and future are mechanisms for transporting
things and exceptions and whatnot
from one thread to another.
So they focus on the threading aspect.
And again, they are very close to what we need.
So, I'm not gonna cease too much
on this union types and then stuff.
But it's got a variable for illustrations.
we're going to have a discriminated union type
which is pretty much one of two types.
But you're u and we have the class either, let's call it.
And the union is going to store one
or the other in the boolion.
The boolion, sadly, is going to cost us a whole word.
Cause of alignment, right?
So, it's going to be like
the maxing of t and u plus alignment
plus the boolion and that kind of stuff.
So, let's define the type.
Which takes a t and an e.
And the t is the expected type
and the e, well, it should have been like e and u.
Yeah, should be like e, expected,
u, unexpected; but it's t and e, sorry.
So, you know, whatever. You're smart.
I mean, you're the best in the world, right?
So, t would be the expected type
and u would be the error.
You know, the unexpected type.
Which is kind of the bad guy in the story.
And once you expose this union
above the overt and covert type
and in the nice, happy case, there's a valid t in there.
And in our happy case, an e is there
explaining why t could not exist.
Kind of, I think it's neat, really.
If at this point you think it's not neat,
please go to another talk!
No, you're gonna hate the rest.
So, it's either a t or "honey, I can explain."
I can explain.
Why the t could not be produced.
See a proposal by Vicente and,
you know what JF stands for?
But, nobody can pronounce his name.
So, he said, "I'm gonna just call myself JF.
"Stupid American." (crowd laughs)
I can make fun!
I can make fun of Americans and Europeans,
I have double citizenship! (crowd chuckles)
I have a Romanian citizenship,
which according, at least to some sources,
is Europe and I have American citizenship;
I'm good, I can criticize!
I'm one-eighth Greek; I can criticize Greeks!
They have big nose!
I have no mercy.
Equal-opportunity offender, okay?
So don't get, don't come near me, okay?
So, these folks took my idea and
they actually went through the ordeal
of making it into a proposal.
The proposal is making nice progress;
I am going to discuss it.
By the way, Jean-Francois, please stand up!
Oh, of course, he's the last seat in the back!
And I told him, I'm gonna stand him up!
Thank you very much!
Give him a hand, please!
(crowd clapping) Thank you.
Alright, so he got the aura, the light on his head.
And he's like a mystical event.
So, their proposal goes as follows.
So, we want to unify the local and centralized
error handling by the following means:
you're going to return and std expected of,
for example, int, and some error type.
And that would be the result of, let's say, atoi ++.
So, if you're local you're going to look,
well, result has value or not?
Say again, this is gonna be a boolian
if, that tells you whether it has a value or not.
And the simplest idiom that you can use
for local error handling is if result use star result.
And that would mean, even though it's not the pointer,
they expect it behaves like a pointer.
You can test it with f and you can actually
be referenced it can use the arrow.
And so, it's kind of a pseudo-pointer, if you wish.
Now, if centralized, you just use
result dot value.
There's a mistake on the slide.
You should use result dot value.
Because, in it's infinite wisdom,
the standardization committee said
"if somebody's going to use star result,
"and the value is not happy, undefined behavior."
Because, apparently there's not
enough of it in the C++ language.
I'm hearing some laughter,
it's not the laughter that I was hoping for.
It's like, "hahaha, yeah I know,
"but I'm gonna kill you after this."
Because there's many committee members
in this room and there's many,
you know, many folks who are pro-
the committee members, so let me criticize.
There is enough undefined behavior
in C++ and you should not have done that.
You feel the tension? (crowd laughs)
It's like really nobody see.
Alright, so anyway.
Essentially, it's like this.
If you say result dot value,
it's going to throw the exception if it's not good.
If you say star the result, undefined behavior.
Which, actually, Jean-Francois was very smart
to say, "you know what, if it's undefined,
"on a good implementation, may as well
"throw the damn e."
(crowd laughs) Right?
And it's all good in the world.
Alright, okay, this is awesome.
I got some good golf-clap.
"Oh yeah, okay, that was okay."
Well, let's look at some characteristics
of expected t and I'm gonna actually go
a bit into the implementation.
So, it associates-- this is the
most beautiful thing because it's like,
in locking it's good to associate
the new text with the directs protecting
and that kind of stuff so this sort of
encapsulation is very, historically,
it's very productive.
And very, kind of good.
Like, you know, motherhood; apple pie.
So it associates the errors, the computational
goals that I want to produce with whatever
else may be created in the process of
attempting that production.
So they're encapsulated together in this expected thing,
which is beautiful; I like it.
It naturally allows multiple exceptions
because the exception is produced but not thrown.
This is really the key to understanding
this whole idiom; which is,
"wait a second, I'm actually creating
"the exception without constructing whatever."
So the exception exists but it is not yet thrown;
it's created now, thrown later, if at all.
So it can actually do error handling
with exceptions without throwing exceptions.
That would have been, actually,
a great moment for the golf-clap
that was before but, you know, anyway.
So, this is the awesomeness of it,
yeah, you can actually, yeah.
So now we can have a million exceptions if we wanted.
We could have a vector of exceptions
or you could have std map of mapping strings
or whatever you want.
And then, you know, we can transport them across
threads; no throw, whatever, across time,
save enough for it later, et cetera, et cetera, et cetera.
Collect group, combine exceptions.
Actually, compose programs that use exceptions.
Because you can't compose by throwing.
You can't build by destroying.
It's basic, right?
It's basic I don't know what. Right?
So, let's look at the implementation.
So, I have a, as expected,
okay that was an unintended pun.
We have a template with two arguments,
t and e and we have a union containing
a t and an e, yay and nay.
And we have a boolion, okay,
which we initialize the optimistic way.
We go with the notion that, by default,
they expect that it's going to happen.
I mean, even the vocabulary goes that way.
The focus structure is going to create
a default initialized t because t is expected, friends.
Thank you very much, right?
There's been, actually, debate about this.
A default construct is expected,
said the pessimistic people.
I'm not sure if it should contain a t
because I didn't tell it to create a t.
And now we have,
the construct that takes a const t,
of course it's going to copy the t into the thing.
Well, now there's a third constructor here.
(speaker trails off)
Okay, I pressed the wrong button.
So, I have the third constructor here,
which is expected in terms of const
unexpected of e, reference rhs.
Where did that unexpected come from
and what is it doing?
Any, if you have an idea, you can raise your hand.
I'm not gonna call you out but raise
your hand if you have an idea why
that could be the case?
Why do I need a wrapper around
the e type that's called unexpected?
I see a couple of hands. Awesome!
Okay, I have news for everybody else.
Because you should be able to have a template
that is, has a, you expect an int
but the error code is also int.
Int is a very, like, it's a classic error code.
Return a result.
So, I could have an, I expect an int
but the error code is also an int.
So then I need to distinguish between
the constructor of the happy case
and the constructor of the unhappy case.
So I'm going to wrap that integer
completely unremarkable type which is called unexpected.
But wait, you ask.
What if I want
to expect the unexpected? (crowd laughs)
That's where the paragraph comes
and says you can't expect the unexpected;
that's illegal use!
By law, it's forbidden.
So, that's very nice, they thought of everything.
But you've gotta have the unexpected type,
it's completely unremarkable and just wraps a type
and it has, like, give me that thing;
and that's the only interface it has.
It's important just to tag the value of e.
Or the type of e.
Anything more interesting here, let's take a look.
We have the move constructor and everything with
universal reference here.
I said universal, not forward.
I'm not gonna comment anymore about that.
We have, kind of a, it's getting more interesting
by each slide, so we have, kind of a,
okay, so let's copy this thing
and if okay, then no need to copy that thing
and otherwise I'm gonna copy the other thing.
If the move constructor, which is along the same lines.
And here's the
Yes, I put in a comment.
If you're a good person, you uncomment that line.
Please comment that line.
Oh, by the way, the argument was efficiency.
So, apparently all that work on branch prediction,
all that good stuff is not,
not good enough.
Anyhow, so we have like a number
of overloads and all the jazz.
I think, actually, in a real,
in Jean-Francois' presentation like
eight of them, quite a few now have
to operate the dereference operator
and we have, give me the error please.
So that would be, I'm fetching the error.
And again, I think it has undefined behavior
if there's no error there.
I have a query.
Which tells me, "well, do you have
"a value or not?"
And is going to simply return whether
the value is happy.
We have the bool operator which
allows us the eve test.
And we have the value, give me the value.
And this, actually, does throw the exception
if it's good.
So you know, value's the right thing to do.
there will be a side discussion that
I would like to have.
Which is, what's the deal with exceptions?
It's not the pointer, but it's,
looks, you know, it acts like a pointer.
It has the start, has the arrow,
and has those pointer-like characteristics.
There's a precedent to that.
Which is called?
(crowd responds softly)
(crowd responds loudly)
Now, there should be, like,
a choir here; optional!
Optional behaves like a, looks like a pointer,
behaves, it's actually an optional value,
but it looks like a pointer in the sense
that it can put star on it, you can put arrow on it,
and it can achieve undefined behavior as much as you want.
There's like any amount of undefined behavior
you can get from optional.
And, it's a precedent; there's no other type
in the standard library that would look
like a pointer but not be a pointer.
And it's kind of, I would say it's an improve on design.
I would say it's, the jury's still out on that one.
But, you know, it was already standardized
and it was there; so they said,
we already made a couple of bad decisions there.
How about we implore them to expect it as well?
This is the true story.
And optional was like the perfect
legal precedent that they said,
"oh! We have all of this bad things here,
"all of these awful things we're doing here.
"Oh, you have this proposal?
"Please do this thing and expect it, as well.
"And we're gonna accept it.
"By the way, you have no power to protest."
This is not approved yet, probably after this talk,
it's never gonna. (crowd laughs loudly)
But you can actually, you can actually,
you know, call your local representative
and tell them, I would like some
more defined behavior, if possible.
I'm hearing something, I'm gonna ignore it.
(Andrei clears throat)
At any rate, the most interesting function
I had to write and actually found a bug
in Vicente and Jean-Francois' proposal.
Which is the swap function,
which is very funny because you must
swap two values that may have actual
different types because one might be
bad and one might be good.
So let's go with, looking at swap,
I'm going to skip the signature for now, friends.
So I'm going to go, see, this like
long enable f t with a complex,
you know simple, quite complex boolian condition, here.
But let's ignore that for a moment.
And let's handle the easy cases.
Let's handle the easy cases.
The easiest case is both are good.
If okay and there are just okay,
then let's do good values and we're going to swap the yays.
if one is, (Andrei fumbles for words),
this is kind of, I'm simplifying my whole life here
because here I say, "ah, I'm gonna do this later."
You know, I'm swapping the swappees.
I'm saying I'm gonna solve this later,
you know in this else case, it means the first
is good and the second is bad.
I am going to say, "oh, let me handle it later,
"so I don't duplicate code."
Then, if my, me, this is not okay.
And rhs not okay, again, I just swap the nays.
So that's, again, easy.
And else, here comes drag ons.
This is the difficult part, the dot, dot, dot
here is the difficult part.
Because I need to swap, I know that,
one object is a t and the other object is an e
and I need to swap them really carefully.
And I look, it's like two drivers,
two cars driving, you know, 100 kilometers,
sorry, 60 miles an hour.
And they need to kind of jump each other
and you know, there's two different drivers
in two different cars; it's a difficult proposition.
So, let's take a look.
And kind of, you know, it takes you a bit of
like, you gotta think of it a bit.
And it's not easy.
So what I did was, "well, let me,
"actually, create a type where
"I'm going to move from my lane."
I'm bad, the other guy's good, right?
So, I'm going to move away from nay
so I free up my object this.
The next thing I'm going to do is, careful here!
I still need to call the destructor of nay.
Even after I move, you still have the civic
duty to call the destructor.
For absolutely no reason because nobody's
gonna be out of their mind to leave
something interesting that needs to be destroyed carefully.
But you don't know that.
So, being politically correct here,
you're going to call the destructor.
After move the object is in a
destroyable state, et cetera, et cetera,
yada, yada, yada; correct?
Okay, now we gotta destroy that guy.
So, at this point,
this, my object, is empty and ready
to receive new content.
What we're going to do now, fine I'm going to
put true in okay, so I'm gonna make it a good object.
And I'm going to, sorry,
before okay, I'm going to transfer
the content of the good object
from rhs into this by means of a move constructor
and a placement new.
I'm sure I'm being followed here,
but I'm talking in a vacuum.
So, please gimme like, a yes.
- [Crowd] Yes!
- Okay, Jean-Francois didn't say it.
I heard you not saying it.
I'm going to, after the move constructor has
succeeded then I can safely adjust
the flag to true and at this point
right after the key goes through,
this is in a good state.
It has the final state that I'm looking for.
After that I'm going to say,
"well, let me go on the other side."
And at this point rhs is,
you know, it's ready to receive anything
so I'm going to call the t destructor
as I said we gotta do that because that's
the nice thing to do.
And then we're going to initialize it from,
by moving from t the temporary we just had on the stack.
And at the last, as the last stack here,
we're going to set the flag to false.
And at this point the swap is done.
You know, you gotta figure that if
any of this was an exception,
It gets pretty tense, right?
Therefore, getting back to the signature,
which I think that this is the bug I found,
the signature was not restrictive enough.
So, you know, it can't really do what I want.
So, enable if t is enabling the function to exist.
If and only if t is moved constructable
and swappable and e is move constructable
All of these conditions must be met.
Hence the conjunction and otherwise
there's no swap; you can't define it.
That's my, this is my, it's not,
it's not impossible somebody may find the,
kind of, a number of methods and tricks and,
kind of, careful copy and move combinations
that are gonna work better,
but this is the best I could get.
So, again, it's a very good homework
for you all to consider looking at.
So now, you know, we have the
we have the section very nice
a typical use, you go, expect
a double runtime error good and
assert star good is 100; it all goes through.
Although nobody should compare double numbers
by equality; you know, it's funny,
whenever I give a Tech talk, you gotta qualify
pretty much every statement you give, you say.
Cause there's all these complications.
Unexpected disambiguates the bad case
so we have the expected constructed
with an unexpected of runtime error.
So Jean-Francois and Vicente did a
very nice thing of having this unexpected thing
be on the same time of functional type, which is clever.
So it's simply just call unexpected here,
without any type or anything it just works.
Very nice and easy to use.
And there's actually, there's some details
in flux, you know, in flux about that particular bit.
A typical use, let's imagine we define
a relative function that takes two doubles
and returns the relative ratio of b to a.
And you, you know, if one is zero
you can't divide by zero again to return unexpected.
And otherwise you simply rely on the default
construction of expected then return a double.
You're done. Very nice.
Centralized error handling goes,
if you like undefined behavior, use operator star.
If you don't use dot value.
And that's pretty much the extent of my theory here
because I'm unsolved like indicated
is I'm gonna actually act calm right now.
E is thrown if I call value against the dot
and that kind of stuff.
So, by the way, this one has undefined behavior
so don't do this; use relative of the one dot value.
Local handling is easy adjust it if it's hard
we have the error and we know what to do.
And of course, expect a bit more cost
because there's a little extra testing involved
but it's all good.
Question for everybody.
You know, once I walk into a forest
and a tree fell, I didn't hear it.
What happens if you have, if you call a function
it fails to produce the result,
it returns an actual exception
but you never look at the result.
So, essentially expect it is a contra that says
it's either a t or an explanation for
the fail of producing said t.
You are with me.
Now, I call a function, gives me unexpected,
and it turns out I don't need it.
Well, if I don't need it I don't
care if it could not be produced.
This is the gist of lazy evaluation.
Well, there's been some discussion about it.
Apparently, some people said,
"Actually, you can't, you gotta,
"like, there's an error that goes unchecked."
And you know, they have an argument.
I agree; they do have an argument.
It's just completely wrong. (crowd laughs)
And by the way, in LLVM you're
gonna find something like that
which has a different name.
But it's kind of along the same lines.
And actually in the destructor if you didn't
check the thing, it's going to?
No, it's not gonna throw.
They know more than that.
They know better than that.
It's going to abort the whole application.
The tree not only was audible,
it was an atomic explosion, okay?
No, I'm not kidding.
This is, like, true.
Truth! I saw it, like, just five minutes before my talk.
Anyhow, to wrap up, we associate with
expected errors with the goals of computation.
And I think that's wonderful.
We naturally allow as many exceptions as you want,
You can teleport across threads of,
you name it, stacks; whatever you want.
You just can't transport exceptions just
like normal values so no need to throw them.
You can save them now, throw them later.
You can't do whatever you want with classy
composition measures that C++ gives you.
You can collect, group, and combine them.
And are much, so much simpler for a compiler
to optimize because it's simple if allows tests.
You've been great.
Thank you very much!
(crowd applauds) Thank you.
Do we have time for questions?
Thank you, yes please.
- [Man] Hi, with exceptions if
a function can throw an exception, E1,
and another function invokes that function,
and can also throw an exception, E2,
then the total set of exceptions
that can be thrown by the second function are E1 and E2.
How do you represent and compose errors
with this type, in general?
- This is very interesting.
So, how do you compose with
a function that may throw different
types of exceptions and how they compose
with given that expected is only one type?
Actually, the initial design was more generous
in that regard; it allowed more composition.
But the current design does not,
the design, my baby would have had,
like, an exception; would have supported an exception.
That said, I notice that the world is
moving from large exception hierarchies
to smaller exception hierarchies.
And there's a bit of a trend in the community
to say, "you know what, we really don't
"need those many exception classes
"because we do one thing with them all.
"We just bring here and we say, 'have a good day.'"
So it's not, you know, I don't think
it's a critical issue but I agree it is an issue.
It's difficult to compose if you have,
like, multiple exception types.
- [Man] If I function has side effects
and it's returning unexpected and it
successfully generates the expected
or maybe it doesn't get to it yet
because one of the side effects fails, what then?
- Yeah, that brings us to,
so, you know, the function has side effects.
It also produces a value but you get,
like, you know, don't check the other set.
There is a bit of a fuzzy area there, right?
That's the gist of.
Well, I should add there is a thing as an expected void.
Which is interesting because I expect nothing.
It's only run for the side error effects of the function.
If a function has a side defect
and this is kind of an imperfect scenario.
But, you know, this is C++.
Imperfect scenarios are the name of the game.
You can't have everything.
I agree there's a fuzzy area there.
Where are the side effects versus the return value?
It applies best to functional-style scenarios.
- [Man] So you observe that it doesn't
really make sense to complain if the expected
object is destroyed without being accessed
because you didn't need the values
so you don't care if it failed.
So, what about expected void?
- Yeah, so expected void would be
that one case in which it's
painfully clear that I'm running the function
just for it's side effects so then,
something must be said about that error.
Yeah, expected void would be the one case
in which, eh, you got to the destructed
didn't check anything it's expected void,
there might be some, there might be some room there.
I agree, thank you.
- [Man] Hey, so, during your talk you
mentioned, you know, exceptions having
a performance cost, which, I didn't
really like that in the ether because
it's just sort of, compared to what,
it's very non-specific.
So, I'm just curious in these cases,
where you've looked at it,
I assume you compared to something.
So, was it a case where an exception
was being used for error handling in a local scope?
And did you look at (Andrei clears throat)
do you think that the results would generalize in,
you know, the situations where exceptions
really shine is if you're throwing,
let's say, an exception of five
call-stack layers, now getting rid of
an exception means adding branches
every single layer of the call-stack.
I mean, I'm skeptical that exceptions
will be slower in the normal path
than error codes in that case.
- I think it's a, yeah, I understand the point.
So, the point it, well, how did you,
what's your baseline when you evaluate?
Well, it's kind of interesting.
The baseline was the same application
written with error codes and enabling exceptions.
So you pay all the costs but you
don't reap the benefits.
- [Man] Oh, I see. - So that,
you see, that makes it a bit unfair to exceptions.
I agree with that.
The ideal test would be, you write a whole
application using one style and you
write a whole application using the other style
and you compare.
That would be difficult.
We didn't run such a test.
- [Man] One of my issues with exceptions,
historically, has been it's difficult
when you're trying to log where errors happened.
Where exactly the error happened if
you're catching at some level up the call-stack.
It seems like with rich composition
with this sort of methodology it would be
possible to inject, you know,
with relatively low (Andrei clears throat)
compiler overhead, something that would facilitate
being able to log an effective stack-trace
of where your error happened even
if you're catching it three or four levels up.
Is that something that's been given
consideration in the design as an
optional way to enable or, you know,
would that be roll your own if you
wanted to enable that sort of functionality?
- Okay, so, let me make sure I understand.
The question is like with, with the Try Catch approach
it's sort of more as a theory to,
on the way up, to kind of print things,
log things, when did this happen?
- [Man] More specifically, if you want to
get the global error handling at a
higher level of the stack, you're giving
up the specificity of exactly where
the exception is from. - Right, thank you.
Yes, with expected this is pretty much a typical scenario
in which you do have that chance
with normal programming means
as opposed to, kind of, as a theory
stack-traces and stuff.
So, I don't, it was not a consideration
but it's a great, it's a great angle
that you have about that because
I think it makes it painfully easy
to just, simply, whenever you feel
you're gonna be able to insert instrumentation.
Say, "oh, this, something bad is happening here
"and I'm going to continue moving upwards."
- [Andre] If you want to force a person
to deal with the excepted, couldn't you just
mark it as a "no-discard?"
There's some, yeah, I think you could do that.
GCC has that, right?
There's an annotation that, today,
there is an annotation that says
no-discard, no-discard and that kind of stuff.
So if you're talking more like competition
with that feature?
- [Andre] No, I'm saying, if you were
worried if you were trying to temporary expected,
it would just disappear if you care about the side effects?
- Ah, okay.
- [Andre] And you force them to deal with the
side effects by marking no-discard.
- So the courier of that would be
expected void would always be no-discard.
Is that along the same?
- [Andre] Well, you mark is as no-discard
so they have to ask it, which would cause it to throw.
- Okay, this is it, so we should chat more
about this right now.
We're out of time here, so no more lunch for you.
Thank you very much!
(crowd applauds) Thank you!