Hello, everyone.
Today we're talking about errors frequently made
by Pharo developers including myself.
We'll see how to find and fix common mistakes faster.
Here we have a bit of code.
When it's executed, a debugger opens
and tells us that the message "self" has been sent to an object
and this object doesn't understand the message "self".
We might say that "self" isn't a message that's sent very often,
so there's probably a mistake somewhere in the code.
Taking a little look at it,
we see there's a period missing,
and so the execution is happening as though
"self" was a message sent as a result of "DiceHandle new".
Since the DiceHandle class doesn't have a "self" method,
the debugger opens.
So the solution is to add this period at the end of the first line.
Another problem we often see
is messages that shouldn't theoretically be combined that are combined.
So here we have an error saying that "includes:ifTrue doesn't exist".
"Includes" exists,"ifTrue" exists, "Includes:IfTrue" doesn't.
Looking closer,
we realise that indeed
the message is being sent "includes:ifTrue"
to receiver "x" with 2 parameters, "33" and a block,
and that doesn't work.
When Pharo sees a key word,
it tries to see all the subsequent key words.
It takes them all and considers it as one message.
So what's missing here
is a pair of parenthesis to say
that the message "ifTrue" is sent as a result of "x includes:33".
In the same way, "assert:includes does not exist",
what we wanted to do was "assert on the result of includes",
so the parentheses are missing here.
Don't hesitate to put parentheses when you have
multiple keyword messages in the same expression,
to delimit them, because Pharo will try to group keywords together
and consider it to be one message.
In this example,
we want to have a collection of numbers in Numbers,
and for the moment, there's only one number there, 35.
However, if we see what's in numbers, its not a collection, it's the number.
It's the number 35, so there's a problem.
In the same way, in this code,
if I send the message "new" to the Dice class
I get the number 6
rather than a 6-sided die.
It's the same problem in both examples.
If we look more closely,
adding "yourself" after "add"
will correct the problem. Why?
Because "add" returns its settings.
So "OrderedCollection new add: 35"
returns 35.
If we add the string to "yourself" we're sure to get the receiver at the end
and Numbers will be a collection of numbers.
So the solution here
is to add "yourself" at the end of each message.
Here's another problem.
Here we have a Book class in "Borrow" method.
When we execute, we get the message
that "nil does not understand ifFalse".
So we send the message "ifFalse" here to nil.
What does that mean? It means that in library
at the value nil, which has the default value of all the variables,
we can say that probably "inLibrary" has never been initialized.
We have to put a default value in that variable.
It's pretty easy to correct
by adding the method "initialize",
which from the creation of each instance of the Book class
will put the value "True"
in the instance variable in Library.
Except that if we execute this code now,
we'll get another error message,
"Class True does not understand ifFalse".
Where does this come from? It's because, what we put here,
is a class.
It's not a Boolean, it's a class.
The Boolean is "true" with a small "t".
Classes generally have a capital letter,
so "True" with a capital is a class,
and "true" with a small "t" is the unique instance of the True class.
Here's another problem.
In the "roll" method in the Dice class we expect,
when we roll a Dice, to get a number
between 1 and the number of faces on the die,
except that here, when we roll the die we get a die
and not the face we landed on.
The method I just showed you is equivalent to the method below.
This means that by default, a method that returns nothing returns "self".
This means our "roll" method, when executed, returns the die
and not the result of sending "roll"...
Not the result of sending "atRandom" to the "faces" collection.
So the same problem in a slightly different example.
Here, we're creating a new method,
in the Dice class,
so in Dice class,
we want to make a new method to create instances in the Dice class,
which initializes by default the number of faces at zero.
If we send the message "new" to the Dice class,
what we'll get is the Dice class itself
rather than a new instance of the Dice class.
So in both cases,
the fact that there's no return in "return self"
and "self" by default is the receiver, in the case of a class method
"self" is the class.
To correct these 2 problems,
we just have to add the caret ^ to return to a specific value.
Next problem,
if this code is executed,
the system seems to be frozen and nothing else happens.
It's impossible to interact with Pharo.
What causes this problem?
It comes from the fact that we're implementing a new method
in Dice class.
"Self" is Dice
and so "self new" will call itself recursively.
The intention here
is to use the creation of instance by default
defined in the Dice superclass,
and then add things in relation to that.
By writing like this, we have an infinite loop,
so we need to replace "self" with "super"
to request the implementation of the superclass.
What you should know,
we all make lots of mistakes.
The ones I've shown you are very frequently made
by all Pharo developers,
so there are things we find very frequently:
missing periods,
parentheses,
missing carets ^,
and "yourself".
Try to use the debugger as much as you can to find
the root of problems. It will really help you.
Don't close it as soon as it opens.
You'll be missing out on a way to fix problems.