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.