Variant Records
Variant Records - Basic Concepts
- Basic Idea: A variant record is a record type that can have
different sets of fields (ie variations) in different variables
- The variations are chosen using a specified field as a tag
- Example: Modeling a transaction:
- All transactions have an amount field
- Variant fields:
- Cash transactions: Discount
- Check transactions: CheckNumber
- Credit transactions: Card Number and Expiration Date
Variant Record for Transactions
-
Here is a variant record implementation of the Transaction type
type PaymentType is (Cash, Check, Credit);
-- The_Type is called the discriminant of the type
type Transaction(The_Type: PaymentType := Cash) is record
Amount: Integer;
case The_Type is
when Cash =>
Discount: boolean;
when Check =>
CheckNumber: Positive;
when Credit =>
CardNumber: String(1..5);
Expiration: String(1..5);
end case;
end record;
Ada Code for Transactions
Motivation for Variant Records
- Ability to have a common type that can be used for a number of different,
related types
- For example, allows for collections (eg array or stack) of Transactions
- Example
a: array(1..100) of Transaction;
t: Transaction;
for i in a'range loop
t := a(i);
case t.the_type is
when cash => ...
when credit => ...
when check => ...
end case;
end loop;
Arrays of Variant Records
- To create an array of variant records, the discriminat must have a default value
- Why: To specify how much space to allocate.
- Example that does NOT compile:
type Kind is (int, flt);
type Int_Or_Float (k : Kind) is record
case k is
when int =>
int_value : Integer;
when flt =>
flt_value : Float;
end case;
end record;
-- This will not compile:
type My_Array is array (1 .. 100) of Int_Or_Float;
The array type above will not compile as is, but the version below will
compile since a default value is given for k:
type Kind is (int, flt);
type Int_Or_Float (k : Kind := int) is record
case k is
when int =>
int_value : Integer;
when flt =>
flt_value : Float;
end case;
end record;
-- Create an array type for the variant record
type My_Array is array (1 .. 100) of Int_Or_Float;
-- Now create
a : MyArray;
-- code to fill up the array goes here ...
-- For example: a(1) := (k => int, int_value => 3);
-- For example: a(2) := (flt, 3.4);
-- For example: a(3) := (int, 4);
-- Of course, filling the array with a loop is more likely
-- This loop will process the array, element by element
for i in a'range loop
case a(i).k is
when int =>
put("It's an integer: ");
put(a(i).int_value);
when flt =>
put("It's a float: ");
put(a(i).flt_value);
end case;
end loop;
Implementation
t := (cash, false);
t := (credit, 100, "12345", "01/05");
t := (check, 1234);
What does this tell us about memory allocation and the
implementation of Variant Records?
Dangers of Variant Records
t := (credit, 100, "12345", "01/05");
if t.discount
...
end if;
t := (cash, false);
put(t.CheckNumber);
Type Safety and Variant Records
- Compile time restrictions
and runtime tests guarantee consistency between variants and
fields
- Compile time:
Descriminant must be given a value when creating an instance of the record
- Compile time:
When changing the descriminant, the entire record must be assigned
- Runtime test: compiler generates code for runtime
tests that verify that the descriminant
is consistent with any fields that are accessed
if someCondition then
t := (credit, ...);
else
t := (cash, ...);
end if;
-- Runtime test required: Is it really a cash transaction?
put(t.discount);
Relation to Subtyping
- How would you solve this problem in Java?
- Later we will consider operations on variant records
and how they relate to the java solution
Variant Records in Other Languages