Feature announcement: Function References and Anonymous Functions

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5752
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Feature announcement: Function References and Anonymous Functions

Beitrag von af0815 »

Dear Free Pascal Community,

The Free Pascal Developer team is pleased to finally announce the addition of a long awaited feature, though to be precise it's two different, but very much related features: Function References and Anonymous Functions. These two features can be used independantly of each other, but their greatest power they unfold when used together ......
Quelle: https://forum.lazarus.freepascal.org/in ... #msg443391

Ok, kann man mir das laaaangsam auf deutsch erklären, das ich das auch verstehe :-) Oder gibt es schon einen deutschen Artikel dazu in der Wiki ? Gefunden habe ich da nichts.

Das das ganze im Trunk ist, ist mir schon klar. Nur noch nicht so wirklich wie das funktioniert bzw. zwas (zu was) man das braucht ?!
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 9869
Registriert: Mo 11. Sep 2006, 19:01

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von theo »

@af0815: Schon klar, dass du auf eine Übersetzung von PascalDragon wartest :wink:, aber eigentlich ist das Thema schon steinalt und die Einführung der Features in Delphi auch.
Manchmal ist es hilfreich, die ursprüngliche Diskussion zu verfolgen, wenn man dahinter blicken möchte.
Habe diese hier gefunden (11 Jahre alt) und sie zeigt, dass es (damals) eine umstrittene Sache war:
https://fpc-pascal.freepascal.narkive.c ... ree-pascal (Achtung: lang!)
S.a. https://docwiki.embarcadero.com/RADStud ... _in_Delphi

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1392
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von corpsman »

Das geilste an einer Anonymen Funktion ist, dass du sie als Parameter in Synchronize übergeben kannst (zumindest unter Delphi), damit wird das Schreiben Von Thread Anwendungen welche mal geschwind was in die LCL ausgibt deutlich "eleganter" ;)
--
Just try it

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1210
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von fliegermichl »

Was soll denn eine anonyme Funktion sein?
Bzw. welche Vorteile hat so etwas?

shokwave
Beiträge: 458
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win10 (L 1.6 FPC 3.0.0)
CPU-Target: i386,x64
Wohnort: Rudolstadt

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von shokwave »

Ich fuchs mich seit einiger Zeit in Java ein und da läuft einem das hin und wieder über den Weg. Soweit ich das bisher verstanden habe geht es auch um CleanCode und das DRY(Don't Repeat Yourself)-Prinzip.

Nehmen wir mal an du hast eine Liste mit Objekten vom Typ Hund und du willst die Liste sortieren. Alphabetisch nach Name, nach Alter, alphabetisch nach Rasse und das ganze jeweils auf- und absteigend. Dann brauchst du 6 Funktionen die praktisch identisch sind, bis auf die eine Zeile, in der entschieden wird, ob das Objekt jetzt kleiner oder größer ist als das andere.
Mit Function References kann ich meiner Sortierfunktion nun eine Funktion übergeben, die diese Entscheidung trifft und benötige die Sortierfunktion daher nur ein mal.
Am Ende habe ich damit eine "große" Sortierfunktion und 6 kleinen "Entscheider"-Funktionen.
Da die kleinen Funktionen durchaus Einzeiler sein können, gibt es anonyme Funktionen. Diese können dann direkt in der Argumentliste, beim Funktionsaufruf, erstellt werden, ohne Funktionskopf.
mfg Ingo

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1210
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von fliegermichl »

Aber das gibt's doch schon. Prozedurale Parameter können auch Funktionen sein.

Warf
Beiträge: 1729
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von Warf »

fliegermichl hat geschrieben:
Fr 27. Mai 2022, 13:33
Aber das gibt's doch schon. Prozedurale Parameter können auch Funktionen sein.
Ja, aber es gibt 3 arten von Funktionspointern, normale funktionen, methoden und geschachtelte funktionen:

Code: Alles auswählen

TNormalFunc = function(AParam: Integer): String;
TNestedFunc = function(AParam: Integer): String is nested;
TMethodFunc = function(AParam: Integer): String of object;
Und diese sind inkompatibel zueinander. Z.B. kannst du einem Button event ausschließlich eine Methode zuweisen und keine Funktion. Function referenzes sind jetzt ein Typ der alles vereinigt. Beispiel:

Code: Alles auswählen

TFunctionType = reference to function(AParam: Integer): String;

var
  func: TFunctionType;

function TestFunc(AParam: Integer): String;
begin
  ...
end;

func := @TestFunc; // Normale Funktion

function TForm1.TestMethod(AParam: Integer): String;
begin
  ...
end;

func := @Form1.TestFunc; // Methode

procedure Test;
function NestedTestFunc(AParam: Integer): String;
begin
  ...
end;

begin
  func := @NestedTestFunc; // Geschachtelte Funktion
end;
Das ist ein unfassbar nützliches feature, denn wenn man verucht hat das vorher zu vereinen musste man jede funktion oder objekt 3 mal überladen.

Beispiel aus einem meiner Projekte: https://github.com/Warfley/ObjPasUtils/ ... ctypes.pas
600 zeilen code nur um function references zu emulieren

Socke
Lazarusforum e. V.
Beiträge: 3101
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von Socke »

fliegermichl hat geschrieben:
Fr 27. Mai 2022, 13:33
Aber das gibt's doch schon. Prozedurale Parameter können auch Funktionen sein.
Jetzt musst du ihnen aber keinen Namen mehr geben. Ob das jetzt ein Vor- oder Nachteil ist, weiß ich noch nicht.

Der wichtige Punkt ist wohl:
PascalDragon hat geschrieben:Like nested functions anonymous functions have access to the symbols (variables, functions, etc.) of the surrounding scope including Self if the surrounding scope is a method. Accessing such a symbol is named “capturing” and is one of the core concepts of anonymous functions.
frei übersetzt hat geschrieben:Wie nested functions (lokale Funktionen/Prozeduren in einerhalb einer Funktion/Prozedur) können anonyme Funktionen auf die Symbole (Variablen, Funktionen etc.) des umgebenden Bereichs zugreifen. Bei Methoden ist der Self-Parameter ebenflals zugreifbar. Auf solche Symbole zuzugreifen nennt man "capturing" und ist eines der Kernkonzepte anonymer Funktionen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Warf
Beiträge: 1729
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von Warf »

Ich bin immernoch unschlüssig was ich von den anonymen Funktionen halten soll, die syntax ist so unfassbar hässlich und umständlich das ich un ehrlich zu sein keinen großen vorteil gegenüber geschachtelten funktionen sehe, die könne auch auf lokale variablen zugreifen und man hat nicht solch wunderbare Konstrukte

Code: Alles auswählen

Filter(Iter(arr), function(const A: Integer): Boolean begin Result := A mod 2 =0; end);
Da hat man nix gewonnen was leserlichkeit angeht

Vor allem grade wenn man mal mit z.b. arrow funktionen aus ich glaub C# vergleicht

Code: Alles auswählen

Filter(Iter(arr), A -> A mod 2 = 0);
Daher ist meine persönliche meinung function references sind echt gut, anonyme Funktionen hingegen, in der form, machen den code nur schlimmer

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5752
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von af0815 »

Also eine VerschlimmBesserung :shock: :D :mrgreen:
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 667
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von PascalDragon »

theo hat geschrieben:
Fr 27. Mai 2022, 10:37
@af0815: Schon klar, dass du auf eine Übersetzung von PascalDragon wartest :wink:, aber eigentlich ist das Thema schon steinalt und die Einführung der Features in Delphi auch.
Ich will aber diesen ganzen Text nicht übersetzen. *heul*
Warf hat geschrieben:
Fr 27. Mai 2022, 14:38
Ich bin immernoch unschlüssig was ich von den anonymen Funktionen halten soll, die syntax ist so unfassbar hässlich und umständlich das ich un ehrlich zu sein keinen großen vorteil gegenüber geschachtelten funktionen sehe, die könne auch auf lokale variablen zugreifen und man hat nicht solch wunderbare Konstrukte

Code: Alles auswählen

Filter(Iter(arr), function(const A: Integer): Boolean begin Result := A mod 2 =0; end);
Da hat man nix gewonnen was leserlichkeit angeht

Vor allem grade wenn man mal mit z.b. arrow funktionen aus ich glaub C# vergleicht

Code: Alles auswählen

Filter(Iter(arr), A -> A mod 2 = 0);
Daher ist meine persönliche meinung function references sind echt gut, anonyme Funktionen hingegen, in der form, machen den code nur schlimmer
Da ich bezüglich der Syntax voll und ganz d'accord bin, ist es in FPC möglich geschachtelte Funktionen an eine Funktionsreferenz zu zu weisen und der Compiler wandelt diese dann transparent passend um, so dass sie den Bereich ihrer ursprünglichen Funktion verlassen kann. Sie verhält sich dabei also wie eine analoge anonyme Funktion (du kannst sie aber auch gleichzeitig weiterhin als geschachtelte Funktion nutzen :mrgreen: )
af0815 hat geschrieben:
Fr 27. Mai 2022, 15:35
Also eine VerschlimmBesserung :shock: :D :mrgreen:
Über die Syntax, wie sie von Delphi gewählt wurde, lässt sich vortrefflich streiten, aber die Prinzipien, die hinter den Funktionsreferenzen und den anonymen Funktionen stehen, erlauben nun einiges an Funktionalität, welches zuvor nur über Umwege möglich war.
FPC Compiler Entwickler

Warf
Beiträge: 1729
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von Warf »

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
Zuletzt geändert von Warf am Fr 27. Mai 2022, 17:29, insgesamt 1-mal geändert.

Warf
Beiträge: 1729
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von Warf »

PascalDragon hat geschrieben:
Fr 27. Mai 2022, 15:45
Da ich bezüglich der Syntax voll und ganz d'accord bin, ist es in FPC möglich geschachtelte Funktionen an eine Funktionsreferenz zu zu weisen und der Compiler wandelt diese dann transparent passend um, so dass sie den Bereich ihrer ursprünglichen Funktion verlassen kann. Sie verhält sich dabei also wie eine analoge anonyme Funktion (du kannst sie aber auch gleichzeitig weiterhin als geschachtelte Funktion nutzen :mrgreen: )
Das scheint so noch nicht ganz zu funktionieren, wenn ich im beispiel oben das folgende schreibe:

Code: Alles auswählen

generic function Partial<TParam1, TParam2>(Func: specialize TBinaryProcedure<TParam1, TParam2>; const AValue: TParam1): specialize TUnaryProcedure<TParam2>;
procedure Helper(const AParam: TParam2);
begin
  Func(AValue, AParam);
end;
begin
  Result := @Helper;
end; 
Bekomm ich "Symbol Helper can not be captured"

Sonst hätte ich das oben schon so gemacht

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5752
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von af0815 »

PascalDragon hat geschrieben:
Fr 27. Mai 2022, 15:45
theo hat geschrieben:
Fr 27. Mai 2022, 10:37
@af0815: Schon klar, dass du auf eine Übersetzung von PascalDragon wartest :wink:, aber eigentlich ist das Thema schon steinalt und die Einführung der Features in Delphi auch.
Ich will aber diesen ganzen Text nicht übersetzen. *heul*
@PascalDragon: Geht das so ? (mit DeepL's Hilfe)
Dateianhänge
Funktionsreferenzen.pdf
Translation
(190.99 KiB) 54-mal heruntergeladen
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 667
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Feature announcement: Function References and Anonymous Functions

Beitrag von PascalDragon »

Warf hat geschrieben:
Fr 27. Mai 2022, 17:28
Das scheint so noch nicht ganz zu funktionieren, wenn ich im beispiel oben das folgende schreibe:

Code: Alles auswählen

generic function Partial<TParam1, TParam2>(Func: specialize TBinaryProcedure<TParam1, TParam2>; const AValue: TParam1): specialize TUnaryProcedure<TParam2>;
procedure Helper(const AParam: TParam2);
begin
  Func(AValue, AParam);
end;
begin
  Result := @Helper;
end; 
Bekomm ich "Symbol Helper can not be captured"

Sonst hätte ich das oben schon so gemacht
Sollte eigentlich tatsächlich gehen... würdest du das bitte mal als Bug melden, damit's nicht vergessen wird?
af0815 hat geschrieben:
Fr 27. Mai 2022, 18:06
PascalDragon hat geschrieben:
Fr 27. Mai 2022, 15:45
theo hat geschrieben:
Fr 27. Mai 2022, 10:37
@af0815: Schon klar, dass du auf eine Übersetzung von PascalDragon wartest :wink:, aber eigentlich ist das Thema schon steinalt und die Einführung der Features in Delphi auch.
Ich will aber diesen ganzen Text nicht übersetzen. *heul*
@PascalDragon: Geht das so ? (mit DeepL's Hilfe)
Gar nicht mal so komplett falsch. Man muss halt nur manche stehenden Begriffe sowie die Codebeispiele durch das Original ersetzen und manches formales „Sie“ bzw. „Ihr“ durch „du“ ersetzen. 😅
FPC Compiler Entwickler

Antworten