Counters: an example of a simple Class


     This bio-recipe illustrates how to create a Class (a data structure with its associated procedures) in Darwin. The example we chose is a Counter. A Counter is an object which stores a number. It is understood that this number is incremented occasionally, that is the purpose of a counter. It should be possible to have as many counters as we want. It is also desirable to be able to associate a name or id to the counter, so that some standard printing will show the name together with its value. All these operations should be designed to be as easy and obvious as possible.

     The definition of the constructor is done as follows:

Counter := proc( title:string, value:numeric ) option polymorphic;
  if nargs=0 then noeval( Counter('',0) )
  elif nargs=1 then noeval( Counter(title,0) )
  else noeval( Counter(title,value) ) fi
end:

The above definition allows us to define counters with no arguments, with just a name (title) or with a name and an initial value. For example:

noname := Counter();
t := Counter('iterations');
t2 := Counter('population',7e9);
noname := Counter(,0)
t := Counter(iterations,0)
t2 := Counter(population,7000000000)

     With this definition we can use the selectors for title and value without any need of defining Counter_select.

t[title];  t2[value];
iterations
7000000000

     We could also increment the values using the selectors, but this requires and assignment and we want a simpler construct.

t2[value] := t2[value]+1;
t2[value] := 7000000001

     Notice that the type definitions in Counter prevent us from assigning incorrect values:

t2[value] := abc;
Error, assigning an entry with incorrect type

     Now, for the updating of the Counters we will extend addition (and subtraction) to modify the Counter with the added value. This will give a very simple and readable syntax, as we will see. So we define the method which will add a number to a Counter as part of the addition extension.

Counter_plus := proc(a,b) option internal;
  if type(a,numeric) then b[value] := b[value]+a
  elif type(b,numeric) then a[value] := a[value]+b
  else error('invalid arguments') fi
end:

With this code, we can add to the counter on the left or on the right. So we do some updates to the counters:

noname+1;  2+noname;  t+1;  t2+1e6;
1
3
1
7001000001

the value returned is the result of the addition, that is the latest value of the counter.

     To erase a counter we can assign 0 to its value, or we can allow multiplication to behave in the same way as we did with addition, so that we can multiply the value of the counter. Multiplying by 0 will erase it and additionally, multiplying by any other number will multiply the counter by that value.

Counter_times := proc(a,b) option internal;
  if type(a,numeric) then b[value] := b[value]*a
  elif type(b,numeric) then a[value] := a[value]*b
  else error('invalid arguments') fi
end:
0*noname;  t*2;
0
2

     Finally we will instruct the system on how to display a Counter and/or how to convert it to a readable string. There are many ways of doing this, we can define Counter_print, and/or Counter_string and/or Counter_printf. Here we will define Counter_string which is one of the most general solutions.

Counter_string := proc( c ) option internal;
 c[title] . ': ' . sprintf( '%g', c[value] ) end:

The formatting of a Counter will be done with a numerical format and we prepend the name of the counter to it, separated with a ": ".

string(t);
iterations: 2

     At this point, there are no more special definitions, so we will like to conclude the class definition with everything that can be done automatically. So we issue a:

CompleteClass(Counter);

     Now the class has been completed and we can perform some more operations on Counters, for example

print(t2);
population: 7.001e+09
type(noname,Counter);
true

     At this point, a program can modify a counter directly by assigning to its components (provided that they are of the right type).

noname[title] := '---';
noname[title] := ---

     If this is undesirable, and the only functions allowed to change the members of the class are the constructors and the methods of the class, then we can issue a Protect command and no other program, except for those already defined, can change the structure.

Protect(Counter);

noname[title] := '-----';
Error, title, 
is protected and cannot be used as a selector for a Counter structure
t2[value] := 0;
Error, value, 
is protected and cannot be used as a selector for a Counter structure

     For further information on object oriented functions, please see the OO help file in Darwin.

© 2006 by Gaston Gonnet, Informatik, ETH Zurich

Index of bio-recipes

Last updated on Wed Mar 29 16:02:23 2006 by GhG