dannym wrote:

Hi,

Am Dienstag, den 08.11.2005, 18:10 -0200 schrieb Felipe Monteiro de
Carvalho:
Hello,

I am trying to understand what exactly generics are. I read the wiki
page, but there are lot's of code examples and very few explanations.
Can someone explain it to me in a (relatively) simple way?

What problem is it trying to solve?

It makes types parametrizable.

For example, if you write a list class, traditional delphi has TList.
However, this TList can only contain TObject, no double/integer/... . Moreover, you can put objects of *differing* (i.e. unrelated) classes
in the same list, which is most of the time a bad bad idea.

Excuse me for resorting to an example again, but it's just easiest to
see:

1) without generics

type
 TAppleList = TList;

 TApple = class
 end;
 TOrange = class
 end;

var
 apples: TAppleList;
 apple: TApple;
begin
 apples.Add(TApple.Create); // works
 apples.Add(TOrange.Create); // works, and is stupid

 apple := apples[0]; // compile error
 apple := apples[1]; // compile error
 apple := apples[0] as TApple; // manual cast, works
 apple := apples[1] as TApple; // compiles, breaks at runtime
end;

Generic types, on the other hand, define just the TList, but do not fix
the contained type in it, but leave it as a parameter to specify later.
2) with generics

type
 TListItem = generic(T) record
   Data: T;
   Next: TListItem(T);
 end;
 PListItem = ^generic(T) TListItem(T);

 TList = generic(T) class
 private
    fHead: PListItem(T);
    fTail: PListItem(T);
 published
    procedure Add(Item: T);
 end;

procedure TList.Add(Item: T);
var
 node: PListItem(T);
begin
 New(node);
 node^.Data := Item;
 node^.Next := nil;

 if Assigned(fTail) then begin
   fTail^.Next := node;
 end else begin
   fHead := node;
 end;

 fTail := node;
end;

type
 TApple = class
 end;
 TOrange = class
 end;

 TAppleList = TList(TApple);

var
 apples: TAppleList;
 apple: TApple;
begin
 apples.Add(TApple.Create); // works
 apples.Add(TOrange.Create); // compile error

 apple := apples[0]; // works
 apple := apples[1]; // not applicable
 apple := apples[0] as TApple; // works, but unneccessary
 apple := apples[1] as TApple; // not applicable
end;

And how do generics relate to interfaces?

interfaces in pascal are mostly runtime-bound. Generics are mostly resolved at compile time. Otherwise quite similar, with the exception that there is no "mother of
everyone" class, that is, a class which is the base class of all other
types, also of i.e. Integer.

i.e.
Integer = class(TAll, IUnknown);
Double = class(TAll, IUnknown);
Boolean = class(TAll, IUnknown);
TObject = class(TAll, IUnknown);
TFoo = class(TObject);

note that I'm not advertising that there should be one, just noting the
facts.
The fact being, if there were one, interfaces would do the same as
generics, just at runtime. Without one, interfaces do nearly the same as generics (just don't work for simple types), but still work only at runtime (at huge "cost").
Doing stuff at runtime slows the program down, and also note that the
more you do at runtime, the more stuff the compiler has to compile in at
each place just in case something x or y happens at runtime, at every
place.
Worse, if the language is not really designed for stuff to be determined
at runtime (i.e. late bound stuff), it sucks. Therefore you have to add
"as TApple". Because the language just doesn't expect that you want it
to automagically upcast back to what it was.

If it were a language designed for stuff to be determined at runtime too
(late bound), from the line

apple := apples[0];

it would automagically generate (invisible for the programmer, but in
the executable):
var temp: TObject; temp := apples[0]; if apple is TApple then apple := temp as TApple
 else
   raise ETypeError.Create...;

that is, it would add code for "runtime type inference".

(Note that the "is TApple" and "as TApple" are the destination type of
the variable "apple", i.e. the compiler still wouldn't know what type is
_in_ the list, it just knows you _want_ to have an TApple. If you instead specified orange: TOrange; and accessed: orange :=
apples[0];, fine, it will cast to TOrange, "you asked for it, you get
it")

Which would be a little better than now, but slower.
(as a side note, note the only reason why anybody bothers with type safe
compiled languages is strong type checking, that is total _compile time_
strong type checking, also known as "if it compiles, it works (mostly)".
If it weren't for that advantage (ok, and the speed advantage when done
right), nobody would use strongly typed languages)

Generics, therefore, move the complexity into the type checker of the
compiler instead, the benefit being generation of faster code,
compile-time (automatically) verifyable code, but at the cost of a
larger executable size.

thanks,

Felipe Monteiro de Carvalho

I hope I didn't commit major blunders in the explaination, but it should
be about right :)

cheers,
  Danny


_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel


perfect!

_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply via email to