jpfollenius
jpfollenius

Reputation: 16612

How to avoid evaluation of TFunc<TProc<T>> when passing as method argument?

I have the following method:

InstanceOfMyType(const Name: String; const Modifiers: array of TFunc<TProc<TMyType>>);

The idea is to allow fluent setup code in unit tests.

The problem occurs when I try to call that method:

// in test helper methods
function TTest.WithSomeProperty: TProc<TMyType>
begin
Result := 
  procedure (MyType: TMyType)
  begin
  // modify MyType instance
  end;      
end;

// in the test
Given;
  InstanceOfMyType('Name', WithSomeProperty);

Is there any way to force the compiler not to evaluate the passed TFunc and produce a compile time error?

[DCC Error] IndexerTests.pas(257): E2010 Incompatible types: 'array of System.SysUtils.TFunc<System.SysUtils.TProc<TMyType>>' and 'System.SysUtils.TProc<TMyType>'

WithSomeProperty is obviously a TFunc<TProc<TMyType>> but the compiler always evaluates it and try to passes the resulting TProc<TMyType> to the method which naturally does not pass compile-time checking.

Upvotes: 2

Views: 1034

Answers (1)

David Heffernan
David Heffernan

Reputation: 612993

This program compiles and outputs 12 on XE2:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

function GetProc: TProc<Integer>;
begin
  Result :=
    procedure(Value: Integer)
    begin
      Writeln(Value);
    end;
end;

procedure CallProc(const GetProcArr: array of TFunc<TProc<Integer>>);
begin
  GetProcArr[0]()(666);
end;

begin
  CallProc([GetProc]);
  Readln;
end.

Normally you are allowed to omit the square brackets when preparing an argument for an open array parameter, and the compiler transforms that into a single element array. But that seems to confuse the compiler with this parameter type.

In XE2,

CallProc(GetProc);

results in the E2010 compile error that you reported.

In XE7

CallProc(GetProc);

compiles but then results in a runtime error. So clearly there are still compiler bugs relating to this undocumented feature to turn single values into single element open array arguments.

Unfortunately this mess is a direct result of the decision by the language designers to allow you to omit the parens on a function call. Whilst that does result in arguably more readable code, it does sometimes leave the compiler in situations where an expression is simply ambiguous. That's the case here and you have to explicitly resolve the ambiguity. Is the benefit of omitting the parens when calling functions worth the downside of this ambiguity? In my view it is not.

Upvotes: 5

Related Questions