Next: , Up: Code blocks (I)

6.6.1 Conditions and decision making

Let's first add some code to keep you from writing too many checks. We will simply update our current method for the Checking class; if you have entered the methods from the previous chapters, the old definition will be overridden by this new one.

     Checking extend [
         writeCheck: amount
            | num |
            (checksleft < 1)
                ifTrue: [ ^self error: 'Out of checks' ].
            num := checknum.
            checknum := checknum + 1.
            checksleft := checksleft - 1.
            self spend: amount
            ^ num

The two new lines are:

        (checksleft < 1)
            ifTrue: [ ^self error: 'Out of checks' ].

At first glance, this appears to be a completely new structure. But, look again! The only new construct is the square brackets, which appear within a method and not only surround it.

The first line is a simple boolean expression. checksleft is our integer, as initialized by our Checking class. It is sent the message <, and the argument 1. The current number bound to checksleft compares itself against 1, and returns a boolean object telling whether it is less than 1.

Now this boolean, which is either true or false, is sent the message ifTrue:, with an argument which is called a code block. A code block is an object, just like any other. But instead of holding a number, or a Set, it holds executable statements. So what does a boolean do with a code block which is an argument to a ifTrue: message? It depends on which boolean! If the object is the true object, it executes the code block it has been handed. If it is the false object, it returns without executing the code block. So the traditional conditional construct has been replaced in Smalltalk with boolean objects which execute the indicated code block or not, depending on their truth-value. 1

In the case of our example, the actual code within the block sends an error message to the current object. error: is handled by the parent class Object, and will pop up an appropriate complaint when the user tries to write too many checks. In general, the way you handle a fatal error in Smalltalk is to send an error message to yourself (through the self pseudo-variable), and let the error handling mechanisms inherited from the Object class take over.

As you might guess, there is also an ifFalse: message which booleans accept. It works exactly like ifTrue:, except that the logic has been reversed; a boolean false will execute the code block, and a boolean true will not.

You should take a little time to play with this method of representing conditionals. You can run your checkbook, but can also invoke the conditional functions directly:

        true ifTrue: [ 'Hello, world!' printNl ]
        false ifTrue: [ 'Hello, world!' printNl ]
        true ifFalse: [ 'Hello, world!' printNl ]
        false ifFalse: [ 'Hello, world!' printNl ]


[1] It is interesting to note that because of the way conditionals are done, conditional constructs are not part of the Smalltalk language, instead they are merely a defined behavior for the Boolean class of objects.