Sven Barth schrieb:

* Description

What are tuples? Tuples are an accumulation of values of different or same type where the order matters. Sounds familiar? They are in this regard similar to records, but it's only the order of an element that matters, not its name. So what does make them special? Unlike records you can only query or set all the elements of a tuple at once. They basically behave like multiple assignments. In effect they allow you to return e.g. a multivalued result value without resorting to the naming of record fields (you'll still need to declare a tuple type) or the need for out parameters. This in turn allows you to use them for example in "for-in" loops.

The lack of element names results in bloated code and runtime overhead. See below.

* Declaration:
[...]

The usage of constructors and destructors also allows a realisation of group assignment:

=== code begin ===

var
  a, b, e: Integer;
  c, d: String;
begin
  a := 42;
  c := 'Hello World';
  (b, d) := (a, c);
  a := 21;
  b := 84;

(a, b) := (b, a); // the compiler needs to ensure the correct usage of temps here!

What will happen here?

At compile time a tuple type (integer; integer) has to be defined, and an instance must be allocated for it. Initialization and finalization information/code must be added if required.

At runtime the arguments are copied into that tuple instance, then copied into the target variables. All "copies" may be subject to type conversions and reference counting.

Consider memory usage and runtime when tuples are nested, or contain large data structures (records, static arrays...).

  a := 42;
  (a, e) := (a * 2, a); // (a, e) should be (84, 42), not (84, 84)

Such code tends to become cryptic with larger tuples.
High level (source code) debugging will be impossible :-(


[...]
* Possible extensions

Note: This section is not completely thought through!

An possible extension would be to allow the assignment of tuples to records and/or arrays (and vice versa). [...]

Without references to distinct tuple elements the coder has to provide local variables for *all* tuple elements, then decompose the *entire* tuple, before access to a single element will be possible. This may be accomplished with less source code when a tuple can be assigned to a record variable, but then it would be simpler to use records *instead* of tuples.

When a record type is modified, during development, all *compatible* tuples and tuple types must be updated accordingly.

* Possible uses

- use for group assignments which can make the code more readable
... or unreadable (see above).

- use for multivalues return values which can make the code more readable (instead of using records or out parameters)

This IMO makes sense only when such tuples are passed along many times, before references to their elements occur. Otherwise full tuple decomposition is required when e.g. only a succ/fail indicator in the result tuple has to be examined.

- use as result value for iterators (this way e.g. key and data of containers can be queried)

This reminds me of SQL "SELECT [fieldlist]", where *specified* record fields are copied. But I wonder how confusion can be eliminated in the order of the tuple elements. Will (k,v) or (v,k) be the right order for key and value? What are the proper types of key and value?


* Implementation notes

Tuples need to pay attention to managed types (strings, interfaces, etc.). Thus an Init RTTI will be required (which needs to be handled by fpc_initalize/fpc_finalize accordingly). It might be worthwhile to add a new node type for tuple constructors/deconstructors (one node type should be sufficient) and handle them in assignment nodes accordingly.

I'd reuse the record type node for that purpose.


* Open issues

Should anonymous tuples (together with tuple constructors) be allowed to participate in operator search as well? This would on the one hand allow the following code, but on the other hand make operator lookup rules less clear (because of assignment compatibility rules):

=== code begin ===

type
  TDoubleVector = tuple of (Double, Double, Double, Double);

operator + (aLeft, aRight: TDoubleVector): TDoubleVector;
// implement by e.g. using SSE instructions

// somewhere else
begin
  (d1, d2, d3, d4) := (d1, d2, d3, d4) + (1.0, 2.0, 3.0, 4.0);
end;

=== code end ===

SSE should be used with array types, where all elements *definitely* have the same type. Then one "+" operator can be implemented for open arrays of any size, what looks quite impossible for tuples.


Conclusion:

IMO tuples are *abstract* templates (mathematical notation) for *concrete* (record...) implementations. I see no need or purpose in the introduction of such an abstract type into any concrete language, except when that languages lacks an already existing record (or equivalent) type.

Nonetheless the discussion revealed some possible improvements of record handling, like default constructors/initializers for records, outside "const" clauses.

DoDi

_______________________________________________
fpc-devel maillist  -  [email protected]
http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply via email to