More Smalltalk

Conditionals

Boolean is a class with two subclasses

Some of the messages that True and False respond to are:

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

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'
A block can be passed as a parameter.

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?"

Create the complex numbers

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 does the arithmetic, but if not then aNumber must be float or fraction so the result will not be an integer. Thus call on aNumber to add self to itself.

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.
The fraction class contains the integers and is contained in float. Its + message is:
+ 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.

Self vs Super

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