On 27.01.2013 02:35, Hans-Peter Diettrich wrote:
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.
I don't see why it should result in bloated code and runtime overhead.
* 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.
And here you are wrong. We have an assignment node with a tuple node on
the left and a tuple node on the right. So the compiler can insert
direct assignments between the corresponding elements on the left and
right maybe with the need to insert a few temp vars to resolve cases
like the above. And even then the optimizer can reduce the amount of
temps. So the amount of temps is normally less or equal to the count of
elements, but often it should be less than the count of elements.
If a tuple node is only on one side of the assignment and the other one
is a load node for a tuple variable then construction/deconstruction
(depending on the side) will take place.
Consider memory usage and runtime when tuples are nested, or contain
large data structures (records, static arrays...).
You'll have similar memory usage and runtime problems when you don't use
tuples...
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 :-(
You can also write cryptic code without tuples. Also why should
debugging become impossible? It's "just" a matter of adding the correct
debug information.
[...]
* 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.
There is always Michael's idea of single element access (which can be
extended by allowing ranges)...
When a record type is modified, during development, all *compatible*
tuples and tuple types must be updated accordingly.
If you modify code it's likely that you need to modify other parts anyway...
* 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?
The order of key and value will depend on the corresponding
implementation. You need to look this up like you need to look up other
things as well. By implementing builtin enumerators and those of the FCL
consistently we might be able to provide a guide for other developers...
And the types of key and value depend on what your key and your value
is. You have the same problem currently as well when you want to find
out what the value type of an enumerator is.
* 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.
You can not reuse a node type that does not exist...
Regards,
Sven
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel