Unions
Unions have the semantics of records, however, only one field in the union can contain data at any particular point in the program’s execution. Unions are safe so that an access to a field that does not contain data is a runtime error. When a union is initialized, it is in an unset state so that no field contains data.
Warning
Unions are currently unstable and may change in ways that will break their current uses.
Union Declarations
A union is defined with the following syntax:
union-declaration-statement:
'extern'[OPT] 'union' identifier { union-statement-list }
union-statement-list:
union-statement
union-statement union-statement-list
union-statement:
variable-declaration-statement
procedure-declaration-statement
iterator-declaration-statement
empty-statement
The identifier following the union keyword defines the name of the
union, while the variable declaration statements define its fields and
their types. Procedure and iterator declaration statements define
methods on the union.
If the extern keyword appears before the union keyword, then an
external union type is declared. An external union is used within Chapel
for type and field resolution, but no corresponding backend definition
is generated. It is presumed that the definition of an external union
type is supplied by a library or the execution environment.
Union Types
A union type is referred to using the identifier representing its name:
union-type:
identifier
This is simpler than class and record types because generic unions are not currently supported.
Union Fields
Union fields are accessed in the same way that record fields are accessed. It is a runtime error to access a field that is not currently set.
Example (fieldAccess.chpl).
union U { var x: int; var y: real; } var u: U; u.x = 3; // sets field x writeln(u.x); writeln(u.y); // runtime error: field y is not set
The currently active field of a union can be queried at runtime with the
getActiveIndex method. If the union is not yet
initialized, then getActiveIndex returns -1.
Example (getActiveIndex.chpl).
writeln(u.getActiveIndex()); // prints -1 u.y = 3.0; // sets field y writeln(u.getActiveIndex()); // prints 1
Each union field also has an associated index, this can be queried by accessing the field name as a member of the union type.
Example (fieldIndex.chpl).
union U { var x: int; var y: real; } writeln(U.x); // prints 0 writeln(U.y); // prints 1
Union fields should not be specified with initialization expressions.
Common Operations
Union Assignment
Union assignment is by value. The active field of the union on the right-hand side of the assignment is assigned to same field of the union on the left-hand side, and this field is made active.
Default Comparison Operators
Default comparison operators are defined for unions such that if no more specific comparison is found, these defaults will be used. These default comparisons are defined using the following signatures:
operator ==(a: union, b: union) : bool
operator !=(a: union, b: union) : bool
When these comparisons are applied to two union values of distinct types, a compiler error is generated.
These default comparisons consider two union values to be equal if (a) both unions have the same active field and (b) the respective values of this field are considered equal using ==. Otherwise they are considered not equal.
Union Pattern Matching
There are two primary ways to perform pattern matching on unions: using a
select statement or using the visit method.
Pattern matching allows users to decompose a union based on the currently
active field.
Example (patternMatchSelect.chpl).
Unions can use a
selectstatement to perform pattern matching on the active field of the union.select u { when U.x { writeln("x is active with value ", u.x); } when U.y { writeln("y is active with value ", u.y); } otherwise { writeln("no field is active"); } }Example (patternMatchVisit.chpl).
The
visitmethod can be used to perform pattern matching on the active field of the union, with an associated visitor functor.u.visit(proc(x: int) { writeln("x is active with value ", x); }, proc(y: real) { writeln("y is active with value ", y); });
It is also possible to check the active field of a union with normal conditionals.
Example (patternMatchConditional.chpl).
if u.getActiveIndex() == U.x { writeln("x is active with value ", u.x); } else if u.getActiveIndex() == U.y { writeln("y is active with value ", u.y); } else { writeln("no field is active"); }
Predefined Routines on Unions
- proc union.getActiveIndex()
Returns the index of the active field in the union. This is the field that was most recently assigned to. If no field has been assigned to, returns -1.
- proc union.visit(funcs ...)
Calls the function corresponding to the active field in the union, passing it the value of that field. The number of functions passed must match the number of fields in the union. Each function should expect an argument of the type of the corresponding field in the union.