views:

188

answers:

2

Consider the following declaration of a generic utility class in Delphi 2010:

TEnumerableUtils = class
public
  class function InferenceTest<T>(Param: T): T;
  class function Count<T>(Enumerable: TEnumerable<T>): Integer; overload;
  class function Count<T>(Enumerable: TEnumerable<T>; Filter: TPredicate<T>): Integer; overload;
end;

Somehow the compiler type inference seems to have problems here:

var
  I: Integer;
  L: TList<Integer>;
begin
  TEnumerableUtils.InferenceTest(I);  // no problem here
  TEnumerableUtils.Count(L);          // does not compile: E2250 There is no overloaded version of 'Count' that can be called with these arguments
  TEnumerableUtils.Count<Integer>(L); // compiles fine
end;

The first call works as expected and T is correctly inferred as Integer.

The second call does not work, unless I also add <Integer> -- then it works, as can be seen in the third call. Am I doing something wrong or is the type inference in Delphi just not supporting this (I don't think it is a problem in Java which is why expected it to work in Delphi, too).

A: 

I'm no delphi expert but from the looks of it L is of type TList and not Integer. The last call .Count<Integer>(L) I assume is some sort of typecast to cast the list into an integer value, though I could be completely wrong here but thats what it looks like from my inexperienced eyes :)

Ugh I took another look at the code and I'm more likely completely off here ;P I need to learn delphi syntax before I jump into stuff like this

Jonas B
+5  A: 

The compiler would need to do pattern matching to infer parameter types; it currently does not. The compiler is limited to quite simple inference - if the parameter type is of a type parameter type, then the compiler can figure it out, but not much beyond that.

The argument type in your example is not a simple type parameter, but is rather a constructed generic type (it's constructed with the method's type parameter T, but it is constructed none the less). The compiler needs to make two inferences in order to find out the value of T. First, it needs to see that the constructed type's generic type is an ancestor of TList<T>'s generic type; and it also needs to match the type parameter T in the constructed type's type parameter list with the concrete type Integer in TList<T>'s ancestor.

Barry Kelly
In which case, would it be too harsh to say that the Delphi type inferencing implementation is just barely good enough to warrant it's inclusion on the box as a "new feature", but not really good enough to be considered meaningfully "feature complete" ?
Deltics
Sometimes "just good enough" really isn't good enough at all - better to not have something at all than to have something that can't quack, doesn't waddle and can only barely swim but never-the-less goes around calling itself a "duck". "Better" in the sense that it wouldn't then lead to this sort of question/confusion which draws attention to the "feature gap" and giving ammunition to those seeking to dismiss Delphi as a has-been/also ran. Better to say: "type inferencing doesn't sit comfortably with the nature of Pascal" - which is, imho, a perfectly true and valid position.
Deltics
Thanks, Barry. Seems I have to live with it then. Maybe next year... ;-) A smallish related question if I may: should I base this utils class on IEnumerable instead of TEnumerable? Seems to be more general to me, and the help states that IEnumerable does not have to be declared on classes which have a GetEnumerator method.
deepc