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-7 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.