Um mal ein Praktisches Beispiel anzubringen, was so vorher z.B. noch nicht ging, das partielle anwenden von funktionen. Also du hast eine funktion die 2 parameter bekommt, und dann erzeugst du daraus eine neue funktion die diese funktion aufruft aber mit einem parameter fix:
Code: Alles auswählen
program Project1;
{$mode objfpc}{$H+}
{$modeswitch functionreferences}
{$modeswitch anonymousfunctions}
uses
SysUtils;
type
generic TBinaryProcedure<TParam1, TParam2> = reference to procedure(const A: TParam1; const B: TParam2);
generic TUnaryProcedure<TParam1> = reference to procedure(const A: TParam1);
generic function Partial<TParam1, TParam2>(Func: specialize TBinaryProcedure<TParam1, TParam2>; const AValue: TParam1): specialize TUnaryProcedure<TParam2>;
begin
Result := procedure(const AParam: TParam2)
begin
Func(AValue, AParam);
end;
end;
procedure LogToFile(const AFile: THandle; const AMessage: String);
var
LogMessage: String;
begin
LogMessage := '[%s] %s%s'.Format([DateTimeToStr(Now), AMessage, LineEnding]);
FileWrite(AFile, LogMessage[1], LogMessage.Length);
end;
var
Log: specialize TUnaryProcedure<String>;
fl: THandle;
begin
// Log to consone out
Log := specialize Partial<THandle, String>(@LogToFile, StdOutputHandle);
Log('Console Log');
// Log to console error
Log := specialize Partial<THandle, String>(@LogToFile, StdErrorHandle);
Log('Error Log');
// Log to file
fl := FileOpen('log.txt', fmOpenWrite);
Log := specialize Partial<THandle, String>(@LogToFile, fl);
Log('File Log');
ReadLn;
end.
Hier gibt es eine allgemeine Log funktion die strings in eine datei loggen kann. Die datei wird im ersten argument übergeben. Wenn dieses argument jetzt partiell angewendet wird, kommt eine spezialisierte Log funktion raus die immer auf diese fixierte Datei logt.
D.h. wenn man z.B. eine logging bibliothek schreibt kann man das benutzen und damit eine Log variable publiushen. Wenn logging deaktiviert ist enthält diese eine Funktion die nichts macht. Wenn Debug logging aktiviert wird die erste oder zweite spezialisierung in dem beispiel oben gewählt und die Funktion logt in die Konsole, und wenn Release logging aktiviert ist wird die letzte option gewählt und die Funktion die in eine Datei logt wird reingeschrieben.
Das erinnert manch einen vielleicht an Vererbung im OOP kontext, das liegt daran das in OOP und Closures (also Funktionen mit kontextabhängigen variablen) im grunde das selbe sind, nur anders betrachtet, allerdings fällt hier auch auf, das das "Funktionale" Beispiel oben extrem schmal ist (angenommen die typen und die funktion partial ist gegeben, denn mindestens ich werd dazu ne bibliothek bauen wo solche funktionen bereitgestellt werden), so ist das tatsächlich nur ein einzeiler für die Partielle Anwendung, während man bei OOP dann eine Überklasse mit virtuellen Funktionen erstellen muss und dann von der erben muss.
Also das was hier in ein paar zeilen geht, würde in OOP deutlich mehr code brauchen