Boolean is a class with two subclasses
ifTrue: and ifFalse:always take a block as an argument. A block always begins with [ and ends with ].
2 = 3 ifTrue: [Transcript cr; nextPutAll: 'Liar']
ifFalse: [Transcript cr; nextPutAll: 'You said it'].
Blocks are not evaluated unless sent the message value
|aBlock|
aBlock := [Date today monthIndex = 12
ifTrue: ['Merry Christmas']
ifFalse: ['Not Christmas yet']].
aBlock value => show it gives 'Merry Christmas'Consider the integer message timesRepeat:
timesRepeat: aBlock
"Evaluate aBlock n number of times,
where n is the receiver."
| anInteger |
anInteger := self.
[anInteger > 0] whileTrue: [
anInteger := anInteger - 1.
aBlock value]
The block is passed to timesRepeat and in the whileTrue: loop, the Block is evaluated.
10 timesRepeat: [Transcript show: 'Hello ']
Passing the buck- Think of yourself as an object: "Why should I do this if someone else can do it?"
How to numbers work together when doing arithmetic? Consider integer addition
+ aNumber
"Answer the sum of the receiver and aNumber."
^aNumber + self
If aNumber is an integer then
Float addition is defined by:
+ aNumber
"Answer sum of the receiver and aNumber."
| result |
aNumber isFloat
ifTrue: [
result := self class basicNew: 8.
FloatLibrary add: self to: aNumber result: result.
^result]
ifFalse: [^self + aNumber asFloat]
In float + asks if aNumber is a Float, if so it creates a new float and calls on a FloatLibrary message add:to:result: to store the value of the addition in result. It returns result.
If the number isn't a float, since float is at the top, convert it to a float and call on add.+ aNumber
"Answer sum of the receiver and aNumber."
^((numerator * aNumber denominator) +
(denominator * aNumber numerator)) /
(denominator * aNumber denominator)
The result of adding a fraction to a float is|aFloat aFraction| aFloat := 1.53. aFraction := 1 / 2. aFraction + aFloat show it gives => 2.03
What happened? Both numerator and denominator are integers, so when numerator * aNumber denominator the message aNumber denominator is actually a message the message Number>>denominator which always returns the integer 1 and the product is 1.
The message aNumber numerator is also the Number>>numerator message which returns the number itself, so the product denominator * aNumber denominator is actually denominator * 1.53. This means integer * is called but it recognizes that 1.53 is not an integer so it returns 1.53 * denominator which is the float * operator. Now we add an integer and a float. Again integer + returns the float + integer (float + ) so the result is a float.
This process shows another important feature of Smalltalk, namely if a class does not have a method that it is asked to perform, it searches the hierarchy to find an instance of the message owned by a parent. If it finds such an instance it uses that method to answer the message.
This presents a problem. What if you create a method that requires a call to a parents method.
I created a class MyTime. MyTime is a descendent of Time with the printOn: message that adds the words "My time is " to the time. I still want the time from Time>>printOn:. How can I get that?
MyTime now => My time is 08:57:28
MyTime>>printOn:
printOn: aStream
"Show call to superior"
aStream nextPutAll: 'My time is '.
super printOn: aStream
Generally the word super is the same as self, except it has the side-effect of causing the method lookup mechanism to start searching for the relevant method in the class above that of the current method.
Suppose we add an instance variable, dumb, to MyTime, how would we initialize MyTime now. If we try creating class message dumb: and execute MyTime dumb: 34; now. The first version of MyTime contains 34 as dumb and 0 as time. Another version of Time is create by now. In this version dumb is nil. We must create MyTime with a single class function dumb: time:
dumb: aNumber time: aTime
"set dumb"
|result|
result := self basicNew dumb: aNumber.
result seconds: aTime asSeconds.
^result
result is an instance of MyTime, so you must use instance functions to set the time. aTime is a time, so you can use the asSeconds message to get the time and the seconds: to set the time in result.
The following initializes MyTime:
MyTime dumb: 34 time: Time now
A new printOn: method is required:
printOn: aStream
"Show call to superior"
aStream nextPutAll: 'My time is ';
cr;
nextPutAll: dumb printString;
nextPutAll: ' is dumb';
cr.
super printOn: aStream
For all of the functions see MyTime.cls
Another method of initializing MyTime is just to use the current time as default:
dumb: aNumber
"Uses now as the default time
sample of use
MyTime dumb: 34
My time is
34 is dumb
12:19:21
"
| result |
result := MyTime now.
result dumb: aNumber.
^result