[fpc-devel] Templates / Generics

Bram Kuijvenhoven kuifwaremailinglists at xs4all.nl
Mon Nov 7 12:36:55 CET 2005

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);

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).



More information about the fpc-devel mailing list