Procedures in records

By R. S. Doiel, 2020-07-07

This is the tenth post in the Mostly Oberon series. Mostly Oberon documents my exploration of the Oberon Language, Oberon System and the various rabbit holes I will inevitably fall into.

In my last post I looked at how Oberon-07 supports the passing of procedures as parameters in a procedure. In this one I am looking at how we can include procedures as a part of an Oberon RECORD.

Let’s modify our module name Noises.Mod to explore this. Copy “Noises.Mod” to “Creatures.Mod”. Replace the “MODULE Noises;” line with “MODULE Creatures;” and the final “END Noises.” statement with “END Creatures.”.


    MODULE Creatures;
    
    (* rest of code here *)

    END Creatures.

The key to supporting records with procedures as record attributes is once again Oberon’s type system. The type Noise we created in the previous post can also be used to declare a record attribute similar to how we use this new type to pass the procedure. In this exploration will create a linked list of “Creature” types which include a “MakeNoise” attribute.

First let’s define our “Creature” as a type as well as a CreatureList. Add the following under our TYPE definition in Creatures.Mod.


    Creature = POINTER TO CreatureDesc;
    CreatureDesc = RECORD
                     name : ARRAY 32 OF CHAR;
                     noises : Noises;
                   END;

Let’s create a new MakeCreature procedure that will create a populate a single Creature type record.


    PROCEDURE MakeCreature(name : ARRAY OF CHAR; noise : Noise; VAR creature : Creature);
    BEGIN
      NEW(creature);
      creature.name := name;
      creature.noise := noise;
    END MakeCreature;

Now lets modify MakeNoise to accept the Creature type RECORD rather than a name and a noise procedure parameter.


    PROCEDURE MakeNoise(creature : Creator);
    BEGIN
      creature.noise(creature.name);
    END MakeNoise;

How does this all work? The two “Noise” procedures “BarkBark” and “ChirpChirp” remain as in our original “Noises” module. But our new MakeNoise procedure looks takes a Creature record rather than accepting a name and procedure as parameters. This makes the code a little more concise as well as lets you evolve the creature record type using an object oriented approach.

Our revised module should look like this.


    MODULE Noises;
      IMPORT Out;
    
    TYPE 
      Noise = PROCEDURE(who : ARRAY OF CHAR);

      Creature = RECORD
                   name : ARRAY 32 OF CHAR;
                   noises : Noises;
                 END;
    
    VAR
      dog, bird : Creature;

    PROCEDURE BarkBark(who : ARRAY OF CHAR);
    BEGIN
      Out.String(who);
      Out.String(": Bark, bark");Out.Ln();
    END BarkBark;
    
    PROCEDURE ChirpChirp(who : ARRAY OF CHAR);
    BEGIN
      Out.String(who);
      Out.String(": Chirp, chirp");Out.Ln();
    END ChirpChirp;
    
    PROCEDURE MakeNoise(creature : Creature);
    BEGIN
      (* Call noise with the animal name *)
      creature.noise(creature.name);
    END MakeNoise;

    PROCEDURE MakeCreature(name : ARRAY OF CHAR; noise : Noise; VAR creature : Creature);
    BEGIN
      NEW(creature);
      creature.name := name;
      creature.noise := noise;
    END MakeCreaturel
    
    BEGIN
      MakeCreature("Fido", BarkBark, dog);
      MakeCreature("Tweety", ChirpChirp, bird);
      MakeNoise(dog);
      MakeNoise(bird);
    END Noises.

Where to go from here? Think about evolving Creatures so that you can create a dynamic set of creatures that mix and match their behaviors. Another idea would be to add a “MutateCreature” procedure that would let you change the noise procedure to something new.

Next and Previous