Blog

Switch-case in Smalltalk

Smalltalk language does not offer with his standard classes a switch-case mechanism. Here is an easy way to implement such a mechanism.

The first step will be to create a class Switch as subclass of class Object and to define three variables: value, satisfied and response.

Object subclass: #Switch
    instanceVariableNames: 'value satisfied response'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Objectspace.net-Controlling Structures'

The variable value will hold the object for which the switch is made. The variable satisfied tells us, if any of the case conditions are met. The variable response will hold the result of the operation, which will be made in case of fulfillment of the conditions of any case.

Next we define a class method for: which creates an instance of the class Switch.

Switch>>for: anObject
   ^self new value: anObject 

To avoid having to initialize the variable satisfied to false, we define a method isSatisfied, which returns true only if the variable satisfied is true.

Switch>>isSatisfied
   ^satisfied == true 

Now we create methods case:then: and default:

Switch>>case: oneArgTestBlock then: execBlock
   "The oneArgTestBlock must return a Boolean value
   when ==passed the value of the receiver."

   self isSatisfied ifFalse:[
      (oneArgTestBlock value: self value) ifTrue:[
         self response: execBlock value.
         self satisfied: true]].
   ^self response

Switch>>default: execBlock
   self isSatisfied ifFalse: [
	self response: execBlock value].
   ^self response 

And now we can write:

(Switch for: x)
   case: [:value | value = 1] then: ["..."];
   case: [:value | value = 2] then: ["..."];
   default: ["..."]  

For such a simple test block as above, we can create a very useful method in class Switch.

Switch>>caseIs: testObject then: execBlock
   ^self
	case: [:value | testObject = value]
	then: execBlock. 

So our previous example will look like this:

(Switch for: x)
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]

We can also define an identity version of this method for fast comparing of symbols or classes:

Switch>>caseIdentityIs: testObject then: execBlock
   ^self
	case: [:value | testObject == value]
	then: execBlock.  

And we can simplify searching in collections:

Switch>>caseIsAny: testCollection then: execBlock
   ^self
	case: [:value | testCollection includes: value] 
	then: execBlock. 

All of this looks good, but someone would like it even easier - instead of writing Switch for: aValue simply write aValue switch. To do this we add the switch method to the class Object.

Object>>switch
   ^Switch for: self 

And our previous example can be written as follows:

x switch
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]  

(pn)

The method switch presented above can also be defined as follows:

Object>>switch
   ^Switch for: self value 

Now it is possible to use the following syntax:

[x\*10] switch
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]  

Besides, this implementation allows to use an instance of the class ValueHolder in the switch-case structure. The method switch can now be sent directly to such an object.

In the above switch-case mechanism we can also remove the variable satisfied. We will then perform several blocks satisfying the case conditions, just as it does in C.

(wh)

Przemysław Nieściór & Wacław Hołub, 02.06.2002


Sources:

or on GitHub:

Posted by Przemysław Nieściór at 11 June 2012, 12:00 pm with tags switch, case, smalltalk link