On Thursday 29 January 2015 01:28:36 Sean Billig wrote:
> Thanks for taking the time to write this all up to keep me in the loop on
> the internal discussions.
Hello everyone,
During the discussion I talked about tagged unions sounding more like
Pascal variant records. Brad asked me to write up something about
those. To me, adding stuff to an enum to make a tagged union sounds
wrong. A tagged union sounds like a record type, not an enumerated
type. (In looking up Rust to see their enum type, I also noticed that
Apple's "new" programming language "Swift" has strong similarities
to Rust, including the enum type extension to have tagged unions.)
Pascal style variant records: (not Pascal syntax,
I tried to use a chapel style syntax.)
enum id_kinds { k_type, k_variable, k_parameter }
enum param_kinds { p_value, p_address, p_name }
record identifier {
var name : string;
var typeinfo: typedescriptor;
select kind : id_kinds {
when k_type { /* nothing */ }
when k_variable { var offset : int; }
when k_parameter { var order : int; var paramKind: param_kinds; }
}
}
// kind ends up as a field in the record.
// This separates the notions of enumerated and record-like structures.
// Also, this is not adding anonymous records, it just
// defines the remainder of the record in these
// cases. It is a single namespace and all
// field names need to be unique.
var r : identifier;
// Accessing
.... some code to set it ....
select (r.kind) {
when k_type do ....; // no access needed
when k_variable do ... r.offset ....;
when k_parameter do { ... r.order .... r.paramKind .... };
}
// If runtime checking, accessing r.offset when r.kind != k_variable,
// generate a runtime error, and so forth...
// setting the fields can have different semantics.
// Method 1: direct setting of the r.kind field.
r.kind = k_variable;
r.offset = 36;
// Could generate a runtime error for r.offset = 36 if
// r.kind is not k_variable.
// Method 2: indirect setting of the r.kind field
// as a side effect of other assignments.
r.offset = 36; // sets r.kind to k_variable
r.order = 3; // sets r.kind to k_parameter
r.kind = k_type; // still need direct setting if value has no fields
// Possible expanded syntax:
record newone {
select tag: int {
when min(t).. 0, 3, 7, 9 { var q: real; }
when 1, 2, 4..6, 8 { var g: boolean; }
otherwise { var other: int }
}
}
// the x .. y allows for a range, the commas allow
// for a list of values all of which have the same structure
//
// Also, notice that this is essentially a tagged union here,
// since there are no other fields except in the "select"
// part of the record.
// Or, if you don't like the {} in the when part, how about
record anotherone {
select who : personenum {
when phil then var x : int;
when tom then var y : real;
when brad then var other: string; var j:complex;
otherwise none;
}
}
// These could be more complicated, that is by
// allowing select inside a when {}.
record nested {
select tag1 : int {
when 1 {
select tag2 : boolean {
when true { ... }
when false { ... }
}
var other : real;
}
when 2 { var two : someclass }
otherwise {} // Nothing in the otherwise case
}
}
// Since a select doesn't add a name space,
// the nested selects wouldn't open new name spaces so
// all field names would need to be unique.
Note: in most object oriented languages, these "variations"
are done by base classes and derived classes. The "tag"
field is automatically created so the class knows which one
it is and can be checked in situations where you have a
pointer to a base class and you want to cast to derived class
(or some such feature).
This kind of a variant record allows one to express variations
in data without the class abstractions. One should even
consider allowing these variant structures in classes
also, providing for multiple ways of expression variation in
data.
The C desigeners decided to just do unions and let the
programmer do the tags ....
struct X {
int a; // I'll call this the tag field
union for_a {
int a;
float b;
}u;
};
But it was all user based. You could do structs in the unions
so you could do essentially a "roll your own" variant records.
The major difference:
In C, the unions are named. This requires notations like:
v.u.a to get to the int a inside the union.
In Pascal, (and as I wrote it above) the names are just part
of the same name space. For nested, you could access them
with names like:
v.tag1
v.tag2
v.two
v.other
For implementation ease, Pascal decided that one could only have
a single variant in a record. (Although you could have variants
nested.) I could see the following structure:
record newrec {
select A : boolean { ...... }
select B : integer { ...... }
}
In the implementation, A and associated fields would take up
the equivalent of the largest storage required for a when clause.
So one could still calculate a fixed offset for the fields.
One could even imagine (painfully) having a hidden pointer in the
record to the base of each variant if one wanted to allow some kind
of record/class creation that uses only the space needed.
In pascal, one could allocate a record of the exact size needed
(thus having only one variant) by doing something like:
ptrvar := new (newrec, true, 37)
providing values for A and B at allocation time. At that point
A and B should be considered constants, although Pascal didn't,
to my knowledge, have a way to enforce the tags being constant.
Adding the select/when to the records and classes sounds to
me to be a much cleaner way to get "tagged Unions" than
adding them to enums.
--Phil
--
Phil Nelson
------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
_______________________________________________
Chapel-developers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/chapel-developers