PDA

View Full Version : What's a Functor, and where can I get one?



Xorcist
16-03-2003, 08:18 PM
Someone mind explaining what a Functor is to me. And when and where I would want to use one?

cairnswm
17-03-2003, 06:00 AM
I searched the net and found:


A functor is a function that can be manipulated as an object.

In Java, functors are typically implemented as interfaces defined by a single, generic member function, and that is the approach taken here.

Functors support and encourage a number of powerful programming techniques including:

programming in a functional style
higher order functions
internal iterators
reuse and specialization through composition rather than inheritance and overloading
generic "callback" or "extension point" APIs
generic "filters" or predicate APIs
many "behavioral" design patterns, such as Visitor, Strategy, Chain of Responsibility, etc.


So effectivly its an Object that takes parameters.....

I suppose in Delphi this would be supported by




Function MyFunctor(Parm1 : Type1) : TMyFunctorObject;
Begin
MyObj := TMyFunctorObject.Create;
Result := MyObj;
End;

....
With MyFunctor(Var1) do
Begin
DoStuff;
End;

Alimonster
17-03-2003, 10:17 AM
I believe that a functor is a concept to create a class that can behave primarily as a "function" - usually, in C++, that would be overloading the () operator to provide function-like semantics for a class (possibly as a base class). C++ does feel a little dirty in this respect, though, because it doesn't provide proper interfaces.

As far as I can tell (and I may be wrong here since I've not really bothered looking into functors yet), functors are best used to pass in as parameters to functions, to provide different behaviours. Here's a Delphi example, although I may have missed the point (not sure):


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;

type
IBinaryFunc = interface
['{11231180-585E-11D7-B787-00D0B7BBC908}']
function DoIt(X,Y: Integer): Integer;
end;

TBinaryAdd = class(TInterfacedObject, IBinaryFunc)
public
function DoIt(X, Y: Integer): Integer;
end;

TBinarySub = class(TInterfacedObject, IBinaryFunc)
public
function DoIt(X, Y: Integer): Integer;
end;

TBinaryMultiply = class(TInterfacedObject, IBinaryFunc)
public
function DoIt(X, Y: Integer): Integer;
end;

TBinaryDivide = class(TInterfacedObject, IBinaryFunc)
public
function DoIt(X, Y: Integer): Integer;
end;

TForm1 = class(TForm)
Button1: TButton;
rgpWhatever: TRadioGroup;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

function TBinaryAdd.DoIt(X, Y: Integer): Integer;
begin
Result := X + Y;
end;

function TBinarySub.DoIt(X, Y: Integer): Integer;
begin
Result := X - Y;
end;

function TBinaryMultiply.DoIt(X, Y: Integer): Integer;
begin
Result := X * Y;
end;

function TBinaryDivide.DoIt(X, Y: Integer): Integer;
begin
Result := X div Y;
end;

function DoStuff(Num1, Num2: Integer; Operation: IBinaryFunc): Integer;
begin
Result := Operation.DoIt(Num1, Num2);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
case rgpWhatever.ItemIndex of
-1: ;
0: ShowMessage(IntToStr(DoStuff(3, 6, TBinaryAdd.Create)));
1: ShowMessage(IntToStr(DoStuff(13, 4, TBinarySub.Create)));
2: ShowMessage(IntToStr(DoStuff(3, 5, TBinaryMultiply.Create)));
3: ShowMessage(IntToStr(DoStuff(12, 4, TBinaryDivide.Create)));
end;
end;

end.

Notice how the behaviour of the function "DoStuff" depends entirely on what function is passed in - you're changing its behaviour in any way you see fit :). Also, the functors are derived from TInterfacedObject so they'll be squished automatically when they go out of scope.

The advantage of using classes as "functions" is that they can retain state if required, which gives them more capabilities than just function pointers.

You could probably take this further - one rocking feature of Delphi is class references ("class of") - for example, TClass. There should be a way to use them (passing in the type of the functor instead of using a case statement on it), though it escapes me at the moment.

Class references are lovely. :cyclops:

[P.S. BlueCat, the pascal forum tag seems to screw up with the above code - it seems to be the interface guid line that's the cause]

ggs
07-05-2003, 08:51 PM
You could probably take this further - one rocking feature of Delphi is class references ("class of") - for example, TClass. There should be a way to use them (passing in the type of the functor instead of using a case statement on it), though it escapes me at the moment.



type
TBinaryFunc = interface(TInterfacedObject, IBinaryFunc)
Constructor create; virtual;
function DoIt(X,Y: Integer): Integer; virtual; abstract;
end;
TBinaryFuncClass = class of TBinaryFunc;
....
function DoStuff(Num1, Num2: Integer; OperationClass: IBinaryFuncClass): Integer;
begin
Result := IBinaryFunc(OperationClass.Create).DoIt(Num1, Num2);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
case rgpWhatever.ItemIndex of
-1: ;
0: ShowMessage(IntToStr(DoStuff(3, 6, TBinaryAdd)));
1: ShowMessage(IntToStr(DoStuff(13, 4, TBinarySub)));
2: ShowMessage(IntToStr(DoStuff(3, 5, TBinaryMultiply)));
3: ShowMessage(IntToStr(DoStuff(12, 4, TBinaryDivide)));
end;
end;


You need the costructor to be virtual, so when you use a class reference it will resolve to the correct constructor.

You can get away with "IBinaryFunc(OperationClass.Create).DoIt" because of 2 reasons:
1) if an exception occurs, the constructor fails and the detsructor is called. Thus the Constructor always returns a valid value, or it raises an exception.
2) IBinaryFunc(OperationClass.Create) makes sure the returned class is treaded as an interface. This will cause the referance count to equal one while DoIt is executing, but as soon as the statement finishes, the reference count is decreased, and the interface freed. You might be able to remove the cast, but I havent compiled that to check to see if that would work.

Also you can have virtual class functions too.


type
TBinaryFunc = interface(TInterfacedObject, IBinaryFunc)
Constructor create; virtual;
class function DoIt(X,Y: Integer): Integer; virtual; abstract;
end;

Then something like "OperationClass.Doit" is valid and will call the correct function.

This is useful in a system when you have a list of class types which you create a new instance of each time you use the class type, but need extra information from each class type but dont want to have to create a new class to contain this information (which is static).

Tobject.ClassName is an example of this.