You can create records that are extensions of other records. This is useful if you have records of different types that share common characteristics. For example, you can declare a record type "publication" with the base characteristics common to all publications:
declare record publication field stream name field stream publisher
You can then create record types for specific types of publications by extending the publication type:
declare record book extends publication field stream author variable field stream year-of-publication field stream ISBN declare record periodical extends publication field integer issues-per-year field stream editor-in-chief field stream ISPN
You can use an extended record type as the base type for creating a more extended (and therefore more specialized type):
declare record novel extends book field stream genre field stream locale variable
A shelf of an extended type can be used in place of a shelf of its base type (or any of its ancestral base types) in most circumstances in which the base type can be used. For instance, if you write a function to print the details of a publication, you can also pass a book, or novel to that function. In the following example, the string source
function pub-info
takes a publication, but the program passes a book and a periodical to it. Since a book and a periodical have all the fields of a publication, they can be used as a publication:
declare record publication field stream name field stream publisher declare record book extends publication field stream author variable field stream year-of-publication field stream ISBN declare record periodical extends publication field integer issues-per-year field stream editor-in-chief field stream ISPN define string source function pub-info value publication p as output p:name || "%n" process local publication pubs variable local book war-and-peace local periodical field-and-stream set war-and-peace:name to "War and Peace" set new war-and-peace:author to "Leo Tolstoy" set field-and-stream:name to "Field and Stream" set field-and-stream:issues-per-year to "12" output pub-info war-and-peace output pub-info field-and-stream
Since a record of an extended type can be used as a record of a base type, you can assign a record of an extended type to a shelf of its base type or to a shelf of any of its ancestor types. Thus you can assign a "novel" record to a shelf of type "book" or a shelf of type "publication":
process local novel bedtime-reading local book work-reading local periodical bathroom-reading local publication reading-material variable set bedtime-reading:name to "The Sinister Pig" set bedtime-reading:publisher to "HarperCollins" set new bedtime-reading:author to "Tony Hillerman" set bedtime-reading:year-of-publication to "2003" set bedtime-reading:ISBN to "006019443X" set bedtime-reading:genre to "mystery" set new bedtime-reading:locale to "Four Corners" set new bedtime-reading:locale to "Mexican border" set work-reading:name to "Internet Programming with OmniMark" ;initialize rest of work-reading set bathroom-reading:name to "Reader's Digest" ;initialize rest of bathroom-reading set new reading-material to bedtime-reading set new reading-material to work-reading set new reading-material to bathroom-reading output "Stuff I'm currently reading:%n" repeat over reading-material output reading-material:name || "%n" again
If you have a shelf of a base type that contains records of extended types, you can safely address the fields that belong to the base type and are therefore common to all extended types. If you want to address fields that are specific to any of the extended types, you first need to determine the precise type of each record on the shelf. You can do this using do select-type
:
repeat over reading-material output reading-material:name do select-type reading-material as r case novel output ", a " || r:genre || " novel%n" case book output "by " repeat over r:author as a output a output "," unless #last again output "%n" case periodical output ", a periodical edited by " || r:editor-in-chief else output "%n" done again
Rather than using do select-type
every time you need to determine the exact type of a record, you can create a collection of overloaded type-specific functions using the dynamic
and overriding
keywords. To do this you define a dynamic function for the base type and an overriding function for each of the extensions of the base type. The following code defines several extensions of publication, and a display function for each one. The repeat loop at the end of the code calls the display function for each items on a shelf of publications. The appropriate overriding function is called dynamically based on the type of each individual item on the shelf:
declare record publication field stream name field stream publisher declare record book extends publication field stream author variable field stream year-of-publication field stream ISBN declare record periodical extends publication field integer issues-per-year field stream editor-in-chief field stream ISPN declare record novel extends book field stream genre field stream locale variable define dynamic string source function display value publication p as output p:name || "%n" define overriding string source function display value novel n as output n:name || " a " || n:genre || " novel%n" define overriding string source function display value book b as output b:name || " by " repeat over b:author as a output a output "," unless #last again output "%n" define overriding string source function display value periodical p as output p:name || ", a periodical%n" process local novel bedtime-reading local book work-reading local periodical bathroom-reading local publication reading-material variable set bedtime-reading:name to "The Sinister Pig" set bedtime-reading:publisher to "HarperCollins" set new bedtime-reading:author to "Tony Hillerman" set bedtime-reading:year-of-publication to "2003" set bedtime-reading:ISBN to "006019443X" set bedtime-reading:genre to "mystery" set new bedtime-reading:locale to "Four Corners" set new bedtime-reading:locale to "Mexican border" set work-reading:name to "Internet Programming with OmniMark" set new work-reading:author to "Mark Baker" ;initialize rest of work-reading set bathroom-reading:name to "Reader's Digest" ;initialize rest of bathroom-reading set new reading-material to bedtime-reading set new reading-material to work-reading set new reading-material to bathroom-reading repeat over reading-material output display reading-material again
Suppose you want to write a function that will add novels to a shelf. You could write the function like this:
define function add-novels into modifiable novel n as local novel x repeat ;populate fields of x set new n to x again
This works so long as you only pass shelves of type novel to the function. However, you may want to be able to pass shelves of type publication or type book to the function. The type novel is an extension of book and publication, so you can place a novel record on a book or publication shelf. However, you cannot pass a book or a publication shelf as a modifiable argument to a function that expects a shelf of type novel. For instance, if you passed it a shelf of type "publication", that shelf might contain a record of type "periodical". The function, however, thinks that the shelf is of type "novel", and a periodical cannot be used as a novel.
To write a function that add novels to a shelf, but that will accept a book or publication shelf as an argument, you must use a write only argument:
define function add-novels into write-only novel n as local novel x repeat ;populate fields of x set new n to x again
With a write-only argument the function is forbidden to perform any operation on the shelf which is specific to the type of the individual items on the shelf. It cannot read or write the fields of individual records or even inquire about their type using do select-type
. All it can do is add items to the shelf, replace an existing item with a new item, or delete an item.
The only way to add a new novel to a write-only argument shelf, therefore, is to create a local variable of type novel, update its properties, and then add it to the write-only argument shelf.