Hi! I've been following the generic discussion with great interest. Here are
some of my thoughts.
Florian Klaempfl wrote:
- we'll use a syntax as close as possible to Chrome, e.g.
type
TList<T> = class
...
end;
I greatly favor this syntaxis above the generic-modifier. It will look at a lot
more familiar to most programmers (due to e.g. C++ and Java), is more compact
and moreover likely to be compatible with other implementations (Chrome, rumors
about Delphi 11).
I assume this also means that the generic type identifier has to be included in
the method implementation, e.g.
TList<T>.Add(AValue: T);
begin
...
end;
This would also allow overloading of generics, i.e. having (generic) classes with different
numbers of type parameters, but the same name. E.g. having both a TList and a
TList<T>. (though a declaration like TList = TList<pointer> could easily solve
this in this case)
How will we deal with restrictions on the type of a generic parameter? Chrome
does this with for example
Dictionary<Key, Value> = public class
where Key is IComparable, Key has constructor, Value is Component;
And will it be possible to have type dependent specializations? E.g.
TList<T>.Add(AValue: T);
...
TList<T: TObject>.Add(AValue: T);
...
This can allow for efficient implementations for certain specific types while
using the same name. Though it more or less violates the write-once advantage
of generics, efficiency and type name uniformity is still available I think.
- instantiation will be only possible in declaration blocks, not in code
blocks:
possible:
var
mylist : TList<integer>;
const
mylist : TList<integer> = nil;
type
mylist = TList<integer>;
forbidden:
procedure p(mylist : TList<integer>);
begin
...
mylist:=TList<integer>.create;
...
end;
This seems like a smart and sufficient solution to avoid the problem of parsing
< tokens in code blocks.
On the other hand, programmers will like it (a lot) more if they don't need to
separately define a type for every generic instantiation. Is the
token-lookahead approach proposed elsewhere in this thread not a sufficient
solution? If it is not very easy, or if we are quite unsure about this for now,
perhaps we can postpone the implementation of in-code(block) generic
instantiation to a later time and first implement generics as above.
Maybe we need an exception to this when supporting sub routine templates:
procedure<T> p(mylist : TList<T>);
begin
...
end;
but this doesn't lead to an ambigious syntax.
Because you can easily decide that a T following a < in the code is a generic
parameter? (I assume you also allow TList<T>.Create in the code block ... above?)
- instantiation steps which require code generation are done after main
program compilation based on information saved in the unit files, this
has some advantages:
- several equal instantiations require only one specialisation
So TList<TSomeNiceObkect> and TList<TSomeOtherNiceObject> share the same code
if nothing particular of the types TSomeNiceObject and TSomeOtherNiceObject is used in the
generic?
That is indeed quite important I think. Container classes often do not assume
anything about the class they store, and otherwise we would get a lot of
duplicate code in the resulting executables (for all different classes, which
in fact all are 4 (or 8) byte pointers).
- it's possible to setup an symbol environment as it has been used in
the template definition, so the cyclic unit use problem is void it
requires though that a lot symbols of the implementation part of a unit
must be written to the unit file if the unit contains a template definition
Will a generic implementation have access to procedures visible at the location where the
generic is instantiated? It might seem at first to be a useful restriction to disallow
this, because otherwise a TList<TMyType> in unit1 could need another specialisation
than TList<MyType> in unit2. But on the other hand, we might just want to allow it!
Example:
In the (imaginary) unit containers there might be a TSortedList<T> class, which uses the
< operator on T. In yourunit you define a type TYourType and an operator < operating on
TYourType. If you now want use TSortedList<TYourType> in yourunit, it will need to pick up
the operator defined in yourunit, which is not visible in the unit containers.
Similar questions might arise with respect to code for Hashing a specific type for
e.g. THashMap<Key, Value>. Possible solutions appearing in my mind are that
(1) a user has to write a function Hash(O: TYourType):integer; for every TYourType, or
(2) adding Hash to TObject (like in Java) or to some class of interface (e.g. IHashable), where we need to be able to impose certain conditions on the generic parameters's types as in Chrome (see above).
Note: Java has the Comparable interface and does not allow overloading of
operators like < iirc (but C++ does of course).
Regards,
Bram
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel