Hi. During this class I'll be showing you exceptions,
which are really powerful in Pharo. I'll give you an overview.
What can we do with exceptions? We can resume them,
restart them, signal them as new exceptions.
I'm only going to show you things that will be useful in a practical way
when first using exceptions,
but it's important that you know that this isn't everything.
There are 2 kinds of exceptions, "Errors", which are often fatal
and "notifications" which we can say we don't need,
they're just for reference.
So, if you're interested in reading
a complete reference on exceptions
there's a whole chapter in the book, "Deep Into Pharo",
which is on the MOOC website and available free online.
So, what are you going to learn? To raise and trap exceptions,
and some nice helper methods we can use every day.
So basically,
everything about this class is on this page.
You're going to install a handler to trap an exception,
you'll signal them,
and we'll have 2 nice messages, which are "ensure" and "ifCurtailed"
So, let's take a look.
Imagine that I wanted to trap an exception.
It's the "on: do:" message.
I make a block, if I've an action, when I have an action,
if ever I have an exception, what do I do?
"Something" and here is the exception, which is passed in argument.
If we look at an example of zero division,
if I do 7 divided by 0...
when I have a zero divide, what do I have to do?
I'll write, "Watch out, there's a zero divide."
The thing to notice here is that an instance of the Exception class
has been passed as an argument,
and I'll be able to question it and send it messages,
because exceptions are objects in Pharo.
I'll send messages in my exceptions
to do something and it will show me 0.
So how do we raise an exception?
Basically, we create an exception and we send it the signal message.
So here, when we want to give a warning I do, "Warning new".
I create an exception, customise it, I put the message I want inside,
and send the Signal message.
When I've an exception to be able to ask the author,
writing the method,
then you'll have another API,
because it's "AuthorNameRequest" that defines this API,
so I've passed "Stef" then I'll do "signal".
What usually happens is that the classes
propose shortcuts to avoid having to create, so I'm sending
"OutOfMemory", that's the class. I send the signal message to the class.
Normally, with warnings, I don't do "Warning new, blah, blah..."
I do "Warning signal" with the text, because it's shorter,
but that depends on your exceptions.
So, since we like agile programming with unit tests,
and test-driven programming,
how can we test that an exception is raised in SUnit?
We're going to use "shouldn't raise" and "should raise".
Let's read this together.
I want to say here, "Look out, I don't want
"an exception to be raised,
"for it to be a SubscriptOutOfBounds.
"if I create a date in February".
Now, if I want to test that I should have an error
when I create a date in the 13th month,
something you don't usually get, in your pay,
then I'll use "should raise",
which means, "Watch out,
"if you create a date in a 13th month, I'll raise an error".
That allows you to create the unit test,
while controlling exceptions.
It's a digression, but it's important
to know there are these 2 little helper methods.
So, what are exceptions in Pharo?
There are errors:
"message not understood", "subscript", "division by zero",
you have "halt", which we'll look at when we come to debugging.
You can put a break point,
which will stop execution of the system.
You have notifications, everything non fatal,
so a deprecation, "don't use this method, use another",
warnings and timedout.
One nice thing is, you have an error message
which describes unhandled errors.
This is what raises the debugger.
We don't normally use it, but it's just to tell you
that unhandled errors are also a class of error.
Exceptions are real objects.
If we look at how the "doesNotUnderstand" message
is defined, you can see it in ProtoObject with your Pharo browser,
you do, "messageNotUnderstood new",
I send a message to the receiver, which is the object, and give a signal.
You get this signal when you send "point new strangeAndBizarre".
It's not known as a message, "strangeAndBizarre"
so it will create this exception.
We have deprecation, for example, which means,
if you use an API which evolves over time
and which Pharo has decided is no longer valid,
in fact...
How do we do that in Pharo? We say, "deprecated: on: in"
Here, I've chosen a clear example,
"menu, item, title" and what does it say?
"Use the method addTitle instead of "title".
How is it implemented?
It's implemented in the following way: "deprecated",
this, "create an exception",
which is called "deprecation"
within which I'll pass it "method, explanation, on, in",
which will give a signal. So it's just to show you that
"deprecation", "messageNotUnderstood", are exceptions we create and raise.
So this is just a detail, you can have exception sets.
You can say, "If I have ZeroDivide, and I have Warning,
or I have Warning, you're going to be able,
to contain the exceptions with a comma", which will create an exception set,
allowing you to say, "I want to react to all these exceptions".
It's very rarely used, it's just for completeness.
Now we're going to look at some very handy methods,
which we often use.
How can I ensure that an expression is always executed,
even if the program fails beforehand?
So the idea is "ensure". What does "ensure" do?
You do something,
and I guarantee that the other block will be executed.
It's very powerful, it lets you to close files automatically.
That's what we see in SpyOn, for the Pharo profiler.
There's a class on it. I say, " I'm starting profiling",
I execute the block I want to profile
and I guarantee myself I'll stop profiling,
even if you've passed me a block with an error.
There's another helper
which is really nice, which is how can I ensure
an expression is executed only if the program fails?
What I'll do is I'll say, "You do something if ever
"something strange happens, I want you to execute this code".
Here I've taken an example in "wait", which is really
very low level, which allows you to change the processor scheduling.
I say, "There's the system, schedule a semaphore,"
I do an operation and if ever something doesn't work,
the system guarantees
that I'll remove it from the scheduler.
These methods are really handy, because they're very powerful.
I've given you a slide to explain how the method lookup works,
in addition to the book.
Now...
just for your information,
the things we can do with exceptions in Pharo,
you can return an alternative result, you can retry, in fact.
You can change the execution of the program and say,
"Act as though the exception didn't happen".
I can resume,
I can say, "No, I don't want to deal with this exception at this level,
"I want to deal with it higher up".
I can resignal an exception,
"No, you raised this exception but it's old, I want a new one"
with resignal.
When you need this it means you know what you're doing,
and you've read the chapter, so just be aware that it exists.
This was just to show you.
I want to return another value from an exception, I do "return",
and you can see that here, I had my block which gave me this value.
I raised an exception, and in the end,
it's this value that was returned. That's with "return".
Now, with "resume", what you're going to have,
"resume" is...
I've my exception which is raised here,
and what I see is that, in my exception, when I do "resume",
it's not this value that's returned, it's the next value, so I say,
"Continue after the exception point", so start again from here,
and that will give me this value.
So now if we look at what you should know:
exceptions are powerful in Pharo, OK, great.
There's a very simple API, it's "signal" to raise an exception,
"on: do:" to trap them,
and you have "ensure" to guarantee that this expression is always executed,
if I have a problem here or not,
and there's this expression, with "ifCurtailed",
which will execute "onProblem" if I've a problem in "doSomething".
That's all you need to know to get started with exceptions.