--- title: "Procedures in records" series: "Mostly Oberon" number: 10 author: "rsdoiel@gmail.com (R. S. Doiel)" date: "2020-07-07" keywords: [ "Oberon", "procedures", "record procedures", "programming" ] copyright: "copyright (c) 2020, R. S. Doiel" license: "https://creativecommons.org/licenses/by-sa/4.0/" --- # Procedures in records By R. S. Doiel, 2020-07-07 This is the tenth post in the [Mostly Oberon](../../04/11/Mostly-Oberon.html) 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](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](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](Creatures.Mod) 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 + Next [Portable Oberon-07](../../08/15/Portable-Oberon-07.html) + Previous [Procedures as parameters](../../06/20/Procedures-as-parameters.html)