Generics proposals
Why
- Typesafety !
- Speed
- Readability
See "Already existing stuff" for possible but lacking ways right now.
Usage example
(dannym proposal)
type TGenericList<T> = class public procedure Add(stuff: T); end;
var intlist: TGenericList<Integer>; begin intlist := TGenericList<Integer>.Create; end;
<plugwash> tmycollection=template(tobject)[x] <plugwash> tintegercollection=tmycollection[integer]
oliebol:
template blaat<t:integer>(a:t); var x : t; begin do something with a, and use t with it end;
Terms
type user:
- the function in the compiler that defines a variable or return type and thus 'uses a (already defined) type'.
immediate type:
- not generic and not specialized, i.e. 'normal type'
generic type:
- template for a class with some unspecified types, never to be filled in into this class type. Only by specialisation.
specialization:
- copy generic type class to new specialized type class
type parameters:
- T and Q, the unknown types for the generic TSomeGeneric<T,Q>
specialized type:
- a generic type with all type parameters know, written into a real class type (tree) and reused if possible
Changes
Changes in class/record definition representation
Each class definition representation has added fields for:
- class instantiation mode 0 immediate type 1 generic type, no instantiation, just generate specialized type 2 specialized type
- when mode 2: generic type uplink (XXXX, what, unitname, typename?) when mode 1: list of specialized types known so far (and where), XXXX list items when mode 0: nothing
Changes in 'type user' compilation (for class/record types only)
Each class type user will have to check mode of the class type.
For the used class type, If mode is immediate
- proceed as always till now.
If mode is specialized
- proceed as always till now - keep in mind the generic type for some checks later (and debugging).
If mode is generic, this:
- check 'list of specialized types' for the type parameter to use. If there is already a specialized type, use that. (given that it is compatible with 'compile parameters' XXXX)
- If not available, clone the generic type in the tree, and fill in the type params with the actual types to use. Generate machine code as normal. Remember the new specialized type for later in the list by the generic type and type params used.
(the generic type is best written to some ppu as parse tree so that it can be refetched for cloning somewhen. To keep it easy, initially it should be limited to having its .pas file around when wanting to use a generic)
type params
Generics store a list of the type params (names).
('T', 'Q')
And Specializations store a list of type mappings from the real types to the type params, in the way of
type T = Integer; Q = Boolean;
of course these are local to this class/record specialization.
Other things to note (for *later*)
What if a method implementation depends on the type used to know how the implementation should look like ?
procedure TGenericList<T is TObject>.Add(value: T); procedure TGenericList<T is Integer>.Add(value: T); ?
or more cryptic like
procedure TGenericList<TObject>.Add(value: T); procedure TGenericList<Integer>.Add(value: T); ?
How to do internal storage classes
type PNode<T> = ^TNode<T>; TNode<T> = record prev,next: PNode<T>; data: T; end;
type TGenericList<T> = class private head, tail: PNode<T>; cnt: Integer; public procedure Add(stuff: T); end; ?
This implies that generics are best implemented both for records and for classes alike! Also implies that pointer types need to support generic too.
Also one specialization should chain other specializations (probably also when deriving from a generic).
How to derive from the generic type
It would be good if generic types could be derived from.
type TGenericEventyList<T> = class(TGenericList<T>) public procedure Add(value: T); override; end; ...
Interfaces should support generics too
type
IBa<T> = interface procedure Add(value: T); end;
TBa<T> = class(...,IBla<T>,IInterface) end;
(what to do about the guids of the specializations?)
Of course this has limitations to check, like
- the generic interface cannot be used, just its specializations
- the specialized interface cannot be used in a generic class interface list
- (the generic interface can be used in a generic class interface list)
- the generic interface itself is invalid to use anywhere (just as generic classes are) except for specializing and deriving from
- normal interfaces cannot derive from generic interfaces
- (generic interfaces can derive from generic interfaces) - given the type params are the same
- specialized interfaces cannot be derived from
Limitation of possible specialisations
type TGenericList<T: TObject or Integer> = ... end;
Probably useful...
What to do with class functions
I dont know a whole lot of how class functions are stored internally. What happens to class functions in generics?
Implementation details
Sanity checks
- Generic types cannot be instantiated from
- (Generic types can be derived from)
- Specialized types cannot be derived from
- Normal types cannot use generic types in its definition
- (Normal types can use specialized types in its definition)
- There are no half specialized types (one type param specified, the other not)
- prevent loops while 'unpacking' specializations (loop counter field in every specialization)
Things to note and not forget
- A class or record generic can use itself as type within its definition
Example:
type TBla<T> = class parent: TBla<T>; end;
- A class or record generic can use another generic as type within its definition
type TBla<T> = class parent: TBlo<T>; end;
Implementation steps
Extend parser to support '<T>' and '<T: bla or bla or bla>' and '<T,Q>' notation
Easy to do. Done.
Change class/record/object/pointertype/interface parser to mark if it is a generic, a specialization or normal
Mediate to do. Have done it for class/object and pointertype. Needs all kinds of clone functions for the defs and syms. fpk says he has patches. waiting.
insert dummy symbols (typesym + typedef) for the type params in the generic
Cannot be inserted into the object because parsing of types within objects has been disabled to make that possible:
type Row = Cardinal; TBla = class Row: Integer; end;
weird..
parse implementation section methods *for the generic*
Is harder than it sounds, because the class/object the method is member of has to be searched for the type symbol for the type params (and these *not* derefed but just used as is).
when parsing generics, use a dummy type in place of the type params.
For that, defer the type checking of the compiler in that case to 'as late as possible' or 'never for the generic' <--- TODO... hard...
Change derivation handlers to do the sanity checks
Should pose no problems other than finding all the places.
Add error messages for it
Change ppu streaming routines to save the tree of generics to the ppu
Change type user to instantiate specialized types for generics when used
Somewhat hard to do. (main work here :))
For the instantiation there is a dummy type sym needed (ok), a new objectdef, and cloning of all the syms and defs (at least proc and property) of the original class/object, and replacing all the type params by the correct types. then resume compiling for the class/object.
Already existing stuff
metaclasses
type TObjectClass = class of TObject; TTypedList = class private Fwanttype: TObjectClass; ... public constructor Create(itemtype: TObjectClass); procedure Add(item: TObject); ... end; constructor TTypedList.Create(itemtype: TObjectClass); begin Fwanttype := itemtype; ... end; procedure TTypedList.Add(item: TObject); begin assert(item.Class = Fwanttype); ... end;
... TSomeStuff = class end; ... Flist := TTypedList.Create(TSomeStuff);
Advantages
- already works
Disadvantages
- slow at runtime
- cannot do simple types
- need to cast all the time even for the supported types (i.e. when reading). eeek.
To be incorperated here
Chat logs
<Synopsis> http://www.eleforum.com/cgi-bin/eleweb_lift?action=3&script=wall&num=18&reversesort=true&type=today&starttime=00:00&endtime=23:59&startdate=01-02-05&enddate=01-02-05 <Synopsis> or <Synopsis> http://neli.hopto.org:3980/~micha/irc-fpc/2005/01/%23fpc.20050102.log