Feature announcement: Function References and Anonymous Functions lazarus / 60-Free Pascal ---------------------------------------------------------------------- PascalDragon · 2022-05-26 19:47 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. These features are based on the work by Blaise.ru, so thank you very much and I hope you're doing well considering the current situation. In the following we'll highlight both features separately and then we'll take a look at using them together. Function References Function References (also applicable names are Procedure References and Routine References, in the following only Function References will be used) are types that can take a function (or procedure or routine), method, function variable (or procedure variable or routine variable), method variable, nested function (or nested procedure or nested routine) or an anonymous function (or anonymous procedure or anonymous routine) as a value. The function reference can then be used to call the provided function just like other similar routine pointer types. In contrast to these other types nearly all function-like constructs can be assigned to it (the only exception are nested function variables (or nested procedure variables or nested routine variables), more about that later on) and then used or stored. Function references are enabled with the modeswitch FUNCTIONREFERENCES (the following examples will assume that this modeswitch is provided). A function reference is declared as follows: REFERENCE TO FUNCTION|PROCEDURE [(argumentlist)][: resulttype;] [directives;] Examples: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProcLongInt = reference to procedure(aArg: LongInt); stdcall;  TFuncTObject = reference to function(aArg: TObject): TObject; Like other function pointer types function references can also be declared as generic: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic TGenericProc = reference to procedure(aArg: T); As you can see, once function references are enabled you can't use the identifier "REFERENCE" as part of an alias declaration without using "&": Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  someref = reference; // will fail  someref = &reference; // correct fix  var  somevar: reference; // will fail  somevar: &reference; // correct fix A function reference variable can then be called like any other function pointer type: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  p: TProcLongInt;begin  p := @SomeLongIntProc;  p(42);end. If a function reference has no parameters then you need to use "()" nevertheless in the FPC/ObjFPC modes like for other function pointer types: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProc = reference to procedure;var  p: TProc;begin  p := @SomeProcedure;  p(); // required  p; // this can be used e.g. in mode Delphiend. Like other function pointer types they can also be declared anonymously as part of a variable, field declaration (but not as part of a paramater declaration): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  f: reference to function: LongInt;  type  TTest = class    f: reference to procedure;  end; They get their great power from a point that is for once not considered an implementation detail: function references are in fact internally declared as reference counted interfaces with a single Invoke() method of the provided signature. So the above examples are in fact declared like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProcLongInt = interface(IInterface)    procedure Invoke(aArg: LongInt); stdcall; overload;  end;    TFuncTObject = interface(IInterface)    procedure Invoke(aArg: TObject): TObject; overload;  end;    generic TGenericProc = interface(IInterface)    procedure Invoke(aArg: T); overload;  end; This has a few implications: in the RTTI this will appear like a normal interfaceit reacts to the $M directive like a normal interfaceit is a managed typeit has no valid GUIDit can be implemented by a classit can be inherited from Especially the last two points are important. That the interface can be implemented means that much more functionality and state can be added to a function reference: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFunc = reference to function: LongInt;   TSomeImpl = class(TInterfacedObject, TFunc)    f: LongInt;    function Invoke: LongInt;  end;  function TSomeImpl.Invoke: LongInt;begin  Result := f;end; var  t: TSomeImpl;  f: TFunc;  begin  t := TSomeImpl.Create;  f := t;  Writeln(f()); // will write 0  t.f := 42;  Writeln(f()); // will write 42  f := Nil; // the usual warnings about mixing classes and interface apply!end. As function references don't have valid GUIDs you can't however use QueryInterface() or the as-operator to retrieve it. Using the low level interface related functions of TObject however will work. An interface that inherits from a function reference is still considered invokable by the compiler, so it can still be used like an ordinary function reference could, but you can also add additional methods including overloads for Invoke itself: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TTest = reference to procedure(aArg: TObject);    TTestEx = interface(TTest)    function Invoke: TObject; overload;  end;  var  f: TTestEx;  o: TObject;begin  f := TSomeImplEx.Create;  o := f();  f(o);end. This is for example described by Stefan Glienke on his blog. His linked example won't work as-is however due to missing functionality in Rtti.TValue. As mentioned initially you can assign a nested function to a function reference, but not a nested function variable. There is no real technical reason for this, but it's instead a design choice based on how function references are assumed to behave: they are assumed to be valid beyond their scope (this will become clearer when combined with anonymous functions in the third part), so they can for example be returned from a function or stored in some class instance and can still be considered valid. However a nested function variable is no longer useable once the function frame it was retrieved has been left (for a nested function the compiler can safely convert it in a way that this is no problem, but for a nested function variable it simply can't). One could argue that the same is true for method pointers and method variables as they aren't callable anymore once their class instance is freed however these are much more common in the Object Pascal world while nested function variables are very seldom used, thus the dangers of the former are much more apparent than the dangers of the later. For this reason assigning nested function variables to function references is prohibited. Anonymous Functions Anonymous Functions (or Anonymous Procedures or Anonymous Routines, in the following simply Anonymous Functions) are routines that have no name associated with them and are declared in the middle of a code block (for example on the right side of an expression or as a parameter for a function call). However they can just as well be called directly like a nested function (or nested procedure or nested routine) would. Anonymous functions are enabled with the modeswitch ANONYMOUSFUNCTIONS (the following examples will assume that this modeswitch is provided). An anonymous function is declared as follows: FUNCTION|PROCEDURE [(argumentlist)][[resultname]: resulttype;] [directives;] [[VAR|TYPE|CONST section]|[nested routine]]* BEGIN [STATEMENTS] END As can be seen an anonymous function looks like a regular function (or procedure or routine) with the most important differences being that it does not have a name and that it isn't terminated by a semicolon (because it's essentially an expression). Because it doesn't have a name for modes that don't have the implicit RESULTvariable it's allowed to explicitely name the result variable (even in modes that do have the RESULT variable) like is the case with operator overloads. It's possible to directly call an anonymous function in which case it essentially behaves like a nested function. 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. Their main use however is when assigning them to one of the various function pointer types: function variables, method variables, nested function variables and function references. However not every anonymous function is assignable to every function pointer type as it depends on which symbols (if any) are captured from the surrounding scope. Unlike for non-anonymous function or method identifiers this assignment is however always done without the "@"-operator, because aside from calling one can't do much else with anonymous functions. An anonymous function that captures no symbols at all (except for global symbols or static symbols) is assignable to all four function pointer types. If the anonymous function captures Self then it is no longer assignable to function variables, but still to the other three. And if it captures any local symbol then it's only assignable to nested function variables or function references. In case of function variables, method variables and nested function variables anonymous functions behave just like their non-anonymous counterparts. The differences appear when they're used with function references which will be highlighted in the next part. But first some examples: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFunc = function: LongInt; var  p: TProcedure;  f: TFunc;  n: TNotifyEvent;begin  procedure(const aArg: String)  begin    Writeln(aArg);  end('Hello World');    p := procedure       begin           Writeln('Foobar');           end;  p();    n := procedure(aSender: TObject);       begin             Writeln(HexStr(Pointer(aSender));           end;  n(Nil);    f := function MyRes : LongInt;       begin             MyRes := 42;         end;  Writeln(f());end. Anonymous Function References As mentioned above the greatest power of the two new features comes when the two are combined: like a nested function an anonymous function can access symbols from the surrounding scope, however unlike for nested functions a anonymous function that has been assigned to a function reference can leave the scope where it has been declared in and it will then take the captured symbols with it. For this purpose any variable or parameter that is captured by an anonymous function will become part of the implicitely created object instance (which shall be considered opaque) that will be assigned to the function reference instead of belonging to the original function. The original function will then reference these symbols using the object instance instead of its stack frame. This has the implication that changes to the symobls will be reflected in all anonymous function that capture that symbol. For example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProc = reference to procedure; procedure Test;var  i: LongInt;  p: TProc;begin  i := 42;  p := procedure       begin             Writeln(i);           end;             p(); // will print 42    i := 21;    p(); // will print 21end; Changes will those also be persistent across calls and different anonymous functions as long as they capture the same symbols: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProc = reference to procedure;  procedure Test;var  i: LongInt;  p1, p2: TProc;begin  i := 42;  p1 := procedure        begin              Writeln(i);                  i := i * 2;            end;             p1(); // will print 42    p2 := procedure        begin                Writeln(i);                end;    p1(); // will print 84  p2(); // will print 168end; The lifetime of managed types captured by anonymous function references will be handled accordingly (they will stay alive as long as at least one anonymous function that has captured them is alive as well), however special care needs to be taken regarding manual memory management: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProc = reference to procedure; function Test: TProc;var  o: TObject;begin  o := TObject.Create;  Result := procedure            begin                        Writeln(o.ClassName);                        end;  o.Free;end; Calling the function reference returned by Test will essentially result in use-after-free. And not freeing “o” at all will result in a memory leak. Compatibility The two features are by and large compatible to Delphi's Anonymous Methods. However FPC allows the assignment of anonymous functions to various function pointer types while Delphi restricts them to function references. Also FPC handles the assignment of function, method and nested function variables to function variables slightly differently. Take the following code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure Foo;begin  Writeln('Foo');end; procedure Bar;begin  Writeln('Bar');end; procedure Test;var  p: reference to procedure;  p2: procedure;begin  p2 := Foo;  p := p2;  p();  p2 := Bar;  p();end; Delphi essentially generates the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure Test;var  p: reference to procedure;  p2: procedure;begin  p2 := Foo;  p := procedure       begin             p2();           end;  p();  p2 := Bar;  p();end; This will result in the following output: Code: [Select]Foo Bar However FPC will generate the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure Test;var  p: reference to procedure;  p2, tmp: procedure;begin  p2 := Foo;  tmp := p2;  p := procedure       begin             tmp();         end;  p();  p2 := Bar;  p();end; This will result in the following output: Code: [Select]Foo Foo This is more consistent with assignments of other function pointer types to function pointer types. The Function References feature is available on all platforms which have the Classes feature available (so essentially everything except AVR) and Anonymous Functions themselves are available on all platforms (excluding the assignment to function references on platforms where these are missing). Yes, this includes platform like DOS where directives like “far” and “near” are handled accordingly (which means that these need to be compatible as well when assigning). As these two features are rather complicated there might still be a huge bundle of bugs lurking around so I ask you to test them to year heart's content and report found bugs to the issues on GitLab so that we can fix as many of them as possible before the next major version (which is not yet planned, so don't worry ). Further RTL enhancements like the declaration of TProc<> or the addition of a TThread.Queue() that takes a function reference will come in the near future now that the basics on the compiler side are done. Maybe we can now also tackle ports of libraries like Spring4D and OmniThreadLibrary. There's also the idea to introduce a syntax to control whether symbols are captured by-reference (as currently) or by-value. Enjoy! ---------------------------------------------------------------------- Okoba · 2022-05-26 19:52 Thank you so much to the team! ---------------------------------------------------------------------- marcov · 2022-05-26 20:09 Are the modeswitches already always on for $mode Delphi ? ---------------------------------------------------------------------- PascalDragon · 2022-05-26 20:13 Quote from: marcov on May 26, 2022, 10:09:52 pmAre the modeswitches already always on for $mode Delphi ? Not yet, I first want to spot any mature hurdles with more testers and then we can make them default before the next major release (though at least they shouldn't affect exisiting code except for the point I had mentioned in my announcement post regarding reference, so we can make them default sooner as well *shrugs*). ---------------------------------------------------------------------- bytebites · 2022-05-27 06:02 Oh, it happened in our lifetime.    Thank you. ---------------------------------------------------------------------- Thaddy · 2022-05-27 11:20 Nice toy! Well done you all. Here's an old Barry Kelly smartpointer example for Delphi that now compiles in FPC:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ifdef windows}{$apptype console}{$endif}{$mode delphi}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}{$warn 5036 off}uses  SysUtils; type  Tproc = reference to procedure;  TLifetimeWatcher = class(TInterfacedObject)  private    FWhenDone: TProc;  public    constructor Create(const AWhenDone: TProc);    destructor Destroy; override;  end; { TLifetimeWatcher } constructor TLifetimeWatcher.Create(const AWhenDone: TProc);begin  FWhenDone := AWhenDone;end; destructor TLifetimeWatcher.Destroy;begin  if Assigned(FWhenDone) then    FWhenDone;  inherited;end; type  TSmartPointer = record  strict private    FValue: T;    FLifetime: IInterface;  public    constructor Create(const AValue: T); overload;    class operator Implicit(const AValue: T): TSmartPointer;    property Value: T read FValue;  end; { TSmartPointer } constructor TSmartPointer.Create(const AValue: T);begin  FValue := AValue;  FLifetime := TLifetimeWatcher.Create(procedure  begin    AValue.Free;  end);end; class operator TSmartPointer.Implicit(const AValue: T): TSmartPointer;begin  Result := TSmartPointer.Create(AValue);end; procedure UseIt;var  x: TSmartPointer;begin  x := TLifetimeWatcher.Create(procedure  begin    Writeln('I died.');  end);end; begin  try    UseIt;    Readln;  except    on E:Exception do      Writeln(E.Classname, ': ', E.Message);  end;end. ---------------------------------------------------------------------- simone · 2022-05-27 13:05 Thanks to the development team. Will this new feature be available in the next release version of fpc? ---------------------------------------------------------------------- PascalDragon · 2022-05-27 13:19 Quote from: simone on May 27, 2022, 03:05:29 pmThanks to the development team. Will this new feature be available in the next release version of fpc? It will be in the next major release which is not yet scheduled. The next release will be a minor release (namely 3.2.4). ---------------------------------------------------------------------- Bi0T1N · 2022-05-27 19:09 I'm glad to see that it has finally arrived in FPC - good work! This should allow us to use several nice Delphi libraries with FPC. I think you can also close the issue now. ---------------------------------------------------------------------- Thaddy · 2022-05-27 19:32 Quote from: Bi0T1N on May 27, 2022, 09:09:59 pmI'm glad to see that it has finally arrived in FPC - good work! This should allow us to use several nice Delphi libraries with FPC. I think you can also close the issue now. Not only that: FPC can do more as per PascalDragon's introductary notes... I have been toying with old D2009 and XE2 examples today and 90% can be made to work in minutes. Much more than I expected. ---------------------------------------------------------------------- edwinyzh · 2022-05-28 03:44 Wonderful! Now all must-have syntax I want from FPC is available! Thank all you guys! ---------------------------------------------------------------------- avk · 2022-05-28 10:34 This is really great news, many thanks to the FPC team! ---------------------------------------------------------------------- Thaddy · 2022-05-28 13:12 If you want to experiment, you need the latest trunk. These defines can help others as well:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ifdef windows}{$apptype console}{$endif}{$mode delphi}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}{$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" The "warn 5036 off" is just to suppress a warning about the $capturer variable: I suppose that will be fixed later? It does not really harm, but I sometimes like to compile with -Sew. Everything else looks OK, except some advanced examples that use extended RTTI, but that is logical and described in the announcement. It just means that some frameworks do not work yet. I have by now tested some 20+ examples from Delphi that do work and some homebrew. ---------------------------------------------------------------------- PascalDragon · 2022-05-28 16:24 Quote from: Bi0T1N on May 27, 2022, 09:09:59 pmI think you can also close the issue now. I wanted to look for that already. Thanks for finding it for me. Quote from: Thaddy on May 27, 2022, 09:32:11 pmQuote from: Bi0T1N on May 27, 2022, 09:09:59 pmI'm glad to see that it has finally arrived in FPC - good work! This should allow us to use several nice Delphi libraries with FPC. I think you can also close the issue now. Not only that: FPC can do more as per PascalDragon's introductary notes... I have been toying with old D2009 and XE2 examples today and 90% can be made to work in minutes. Much more than I expected. The other 10% would probably be interesting. At least if they don't rely on feature that we currently don't support (extended RTTI, some of the TValue functionality, etc.). Quote from: Thaddy on May 28, 2022, 03:12:27 pmThese defines can help others as well:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ifdef windows}{$apptype console}{$endif}{$mode delphi}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}{$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" The "warn 5036 off" is just to suppress a warning about the $capturer variable: I suppose that will be fixed later? It does not really harm, but I sometimes like to compile with -Sew. And why, pray tell, did you not report this? This is the first time I hear of this considering that I wrote a ton of tests... Do you have an example where this happens? ---------------------------------------------------------------------- Thaddy · 2022-05-28 18:19 Of course, Sarah. See my above Barry Kelly example and comment the warn  But a great achiviement. The warnings are on: testanon.pas(51,1) Warning: (5036) Local variable "$Capturer" does not seem to be initialized And on line 61 the same. "$Capturer" is not accessible for the normal user. There is also a note, but I tend to ignore those. For completeness: "testanon.pas(60,3) Note: (5027) Local variable "x" is assigned but never used" which is not true... ---------------------------------------------------------------------- AFFRIZA 亜風実 · 2022-06-01 05:56 Nice, can't wait to test that. ---------------------------------------------------------------------- PascalDragon · 2022-06-01 06:54 Quote from: Thaddy on May 28, 2022, 08:19:38 pmThe warnings are on: testanon.pas(51,1) Warning: (5036) Local variable "$Capturer" does not seem to be initialized And on line 61 the same. Can it be that you compile with -O3? (Cause there's another report about this, but without optimizations enabled I can't reproduce this) ---------------------------------------------------------------------- Thaddy · 2022-06-01 08:41 I compiled with -O4 and win64. It should be reproducible in main. With -02 there is no warning. On linux32/64 debian the warning is also there with -O4 Nothing important since the compiler is built with -Sew and -O2 and that gives no warnings. ---------------------------------------------------------------------- PascalDragon · 2022-06-01 11:37 Quote from: Thaddy on June 01, 2022, 10:41:24 amI compiled with -O4 and win64. It should be reproducible in main. With -02 there is no warning. On linux32/64 debian the warning is also there with -O4 Then, next time, please also mention what settings you use to compile, cause that can make a significant difference. ---------------------------------------------------------------------- JdeHaan · 2022-06-17 08:58 Hi, first of all, great to have this new functionality and using it already in several places in my code. I have a small (for me unsolvable) issue, and was hoping to use the function references/anonymous functions to find a solution. What is the problem? I’m trying to convert a C++ macro to a Pascal function: #define MEM(allocator, …) (someFunctionCall(), allocator(__VA_ARGS__)) it is called as follows: MEM(allocString, “Hello world”); MEM(allocCell, value); MEM(allocFunction, codeObject); The result of the MEM call will be a ‘Value’ type. so, the macro allows to call different functions with different arguments. The functions are defined as: Value allocString(std::String string) Value allocCell(Value value) Value allocFunction(CodeObject codeObject) Any idea how to convert this to Object Pascal? Regards, Jeroen ---------------------------------------------------------------------- marcov · 2022-06-17 10:41 I don't think this is possible. Except maybe with an external macro package like M4. Macro shenanigans are simply intrinsicly unportable between languages as they operate on the text of the code. ---------------------------------------------------------------------- PascalDragon · 2022-06-17 11:24 Quote from: JdeHaan on June 17, 2022, 10:58:09 amAny idea how to convert this to Object Pascal? Under the assumption that the allocators only take one argument you can do it like this (this requires main due to the implicit function specialization): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program ttest; {$mode objfpc}{$H+}{$modeswitch implicitfunctionspecialization} type  Value = record  end;   generic TAllocator = function(const aArg: T): Value; procedure SomeFunctionCall;begin end; generic function MEM(allocator: specialize TAllocator; const aValue: T): Value; inline;begin  SomeFunctionCall;  Result := allocator(aValue);end; function AllocString(const aStr: String): Value;beginend; function AllocCell(const aCell: Value): Value;beginend; function AllocFunction(const aCodeObject: CodePointer): Value;beginend; var  v: Value;begin  v := MEM(@AllocString, 'Hello World');  v := MEM(@AllocCell, v);  v := MEM(@AllocFunction, CodePointer(@SomeFunctionCall));end. At least assuming I understood correctly what you're trying to do... ---------------------------------------------------------------------- JdeHaan · 2022-06-17 12:11 Yes, that's pretty much what I need, thanks, I'll give it a try! So, nothing to do with function references, as mentioned in the new feature... ---------------------------------------------------------------------- Ryan J · 2022-06-19 03:56 QuoteUnder the assumption that the allocators only take one argument you can do it like this (this requires main due to the implicit function specialization): Firstly I just joined the forum and I couldn't figure out how to reply by quoting selected text. Is this possible? I just actually posted this exact same question on the FPC mail list before I saw this post. So it works with only one argument but why are arguments not allowed? The type is an undefined generic so it should work in theory and only consider the parameters when the function is specialize ---------------------------------------------------------------------- PascalDragon · 2022-06-19 11:12 Quote from: Pascal Gladiator on June 19, 2022, 05:56:58 amQuoteUnder the assumption that the allocators only take one argument you can do it like this (this requires main due to the implicit function specialization): Firstly I just joined the forum and I couldn't figure out how to reply by quoting selected text. Is this possible? You just did? Quote from: Pascal Gladiator on June 19, 2022, 05:56:58 amI just actually posted this exact same question on the FPC mail list before I saw this post. So it works with only one argument but why are arguments not allowed? The type is an undefined generic so it should work in theory and only consider the parameters when the function is specialize It's possible for more arguments as well, but it means that you need to add overloads of MEM that uses different types of function variables. E.g. like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  generic TAllocator1 = function(const aArg1: T1): Value;  generic TAllocator2 = function(const aArg1: T1; const aArg2: T2): Value; generic function MEM(allocator: specialize TAllocator1; const aValue1: T1): Value; inline;generic function MEM(allocator: specialize TAllocator2; const aValue1: T1; const aValue2: T2): Value; inline; ---------------------------------------------------------------------- Ryan J · 2022-06-20 02:47 For quoted reply I could copy and pasted but notice how it doesn't say who the quote was from. I expected to select some text and then hover the mouse over it and have an option to quote the selection. Pressing the quote button quotes the entire message which I think need to edit by hand. The question was more about the undefined type and adding parameters like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i generic procedure Perform(func: T);beginfunc(1);end; I replied and CC'd you on the mail list also since we're both having problems getting messages. I'm using a gmail account btw because my private domain was blocked (Jonas discovered this some years ago) but I think that's our problem. Other users reported this also so maybe Gmail is the culprit for them also. ---------------------------------------------------------------------- PascalDragon · 2022-06-20 11:36 Quote from: Pascal Gladiator on June 20, 2022, 04:47:23 amFor quoted reply I could copy and pasted but notice how it doesn't say who the quote was from. I expected to select some text and then hover the mouse over it and have an option to quote the selection. Pressing the quote button quotes the entire message which I think need to edit by hand. Correct. You quote whole and then edit by hand. Quote from: Pascal Gladiator on June 20, 2022, 04:47:23 amThe question was more about the undefined type and adding parameters like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i generic procedure Perform(func: T);beginfunc(1);end; Then you should have mentioned that in the first place, cause I thought you were referring to what I had written to JdeHaan. Quote from: Pascal Gladiator on June 20, 2022, 04:47:23 amI replied and CC'd you on the mail list also since we're both having problems getting messages. I'm using a gmail account btw because my private domain was blocked (Jonas discovered this some years ago) but I think that's our problem. Other users reported this also so maybe Gmail is the culprit for them also. It has something to do with SPF records and the domain we send e-mails from. We're already trying to solve this, but as documentation is a bit sparse it's a bit hit-and-miss. ---------------------------------------------------------------------- JdeHaan · 2022-06-21 20:16 Again thank you PascalDragon for the examples. I did need 1 or 2 arguments. Above that I needed it to be part of an advanced record. The following code runs now perfectly: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$H+}{$ModeSwitch advancedrecords}{$ModeSwitch implicitfunctionspecialization} type  generic TAllocator1 = function(const aArg1: T1): TValue;  generic TAllocator2 = function(const aArg1: T1; const aArg2: T2): TValue;   TMyRec = record    procedure someProc;    generic function MEM(allocator: specialize TAllocator1;       const aValue1: T1): TValue;    generic function MEM(allocator: specialize TAllocator2;       const aValue1: T1; const aValue2: T2): TValue;  end; implementation generic function TMyRec.MEM(allocator: specialize TAllocator1;   const aValue1: T1): TValue; begin  someProc;  Result := allocator(aValue1);end; generic function TMyRec.MEM(allocator: specialize TAllocator2;   const aValue1: T1; const aValue2: T2): TValue;begin  someProc;  Result := allocator(aValue1, aValue2);end;  and then calling the respective MEM functions with 1 or 2 arguments from other functions in the record: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Result := MEM(@allocString, 'Hello world!');Result := MEM(@allocCell, Value); Result := MEM(@allocFunction, Name, Arity);  Really nice feature! ---------------------------------------------------------------------- PascalDragon · 2022-06-22 07:01 Quote from: JdeHaan on June 21, 2022, 10:16:04 pmReally nice feature! You're welcome. Please note that you need the ImplicitFunctionSpecialization modeswitch only in the unit where you call MEM, not where you declare it (I know you said that you call MEM from other methods of the same record, so for your real code this doesn't apply, but I just wanted to be sure that you're aware of this). ---------------------------------------------------------------------- JdeHaan · 2022-06-22 10:25 Noted, thanks! ---------------------------------------------------------------------- VTwin · 2022-07-08 00:02 Please forgive my ignorance, but how are function references superior to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TPoint = record    X : double;    Y : double;  end;  TDistanceFunc = function(p, q: TPoint): double; ---------------------------------------------------------------------- alpine · 2022-07-08 12:44 Quote from: VTwin on July 08, 2022, 02:02:16 amPlease forgive my ignorance, but how are function references superior to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TPoint = record    X : double;    Y : double;  end;  TDistanceFunc = function(p, q: TPoint): double; It has something to do with the variable binding I believe. ---------------------------------------------------------------------- PascalDragon · 2022-07-08 15:43 Quote from: VTwin on July 08, 2022, 02:02:16 amPlease forgive my ignorance, but how are function references superior to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TPoint = record    X : double;    Y : double;  end;  TDistanceFunc = function(p, q: TPoint): double; Function references can take additional context. Assume that TDistanceFunc is declared as a reference to function instead of a function and you can do the following (stupid example): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function OffsetBy(const aPoint: TPoint): TDistanceFunc;begin  Result := function(aArg1, aArg2): Double;    var      v: TPoint;    begin      v.x := aArg1.X - aArg2.X + aPoint.X;      v.y := aArg1.Y - aArg2.Y + aPoint.Y;          Result := Sqrt(Sqr(v.x) + Sqr(v.y));     end;end; var  f: TDistanceFunc;begin  f := OffsetBy(Point(10, 20));  Writeln(f(Point(5, 3), Point(6, 8));  f := OffsetBy(Point(5, -1));  Writeln(f(Point(5, 3), Point(6, 8));end. ---------------------------------------------------------------------- alpine · 2022-07-08 16:36 @VTwin Function references encapsulates the function together with it's lexical context (at least - part of) which is also knows as a "closure" (C#, C++). May be the  most familiar example in FPC is the method delegate (procedure of object), which keeps together the method address and the Self pointer of the instance it belongs. Thus, the receiver object can execute the method in the context of the original object. Function references just broadens the context by being able to keep it bigger - including variables from the current scope, etc. But the devil is into the details, and I wonder how useful it will be in practice considering the dynamic nature of the class instances in FPC. ---------------------------------------------------------------------- VTwin · 2022-07-08 16:54 Thanks! I appreciate the example PascalDragon. ---------------------------------------------------------------------- Warfley · 2022-07-08 18:35 To give a more practical example about this, with such a feature you can generalize partial function application which can be used to specialize functions: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}uses  SysUtils; type  generic TBinaryProcedure = reference to procedure(const A: TParam1; const B: TParam2);  generic TUnaryProcedure = reference to procedure(const A: TParam1); generic function Partial(Func: specialize TBinaryProcedure; const AValue: TParam1): specialize TUnaryProcedure;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;  fl: THandle;begin  // Log to consone out  Log := specialize Partial(@LogToFile, StdOutputHandle);  Log('Console Log');  // Log to console error  Log := specialize Partial(@LogToFile, StdErrorHandle);  Log('Error Log');  // Log to file  fl := FileOpen('log.txt', fmOpenWrite);  Log := specialize Partial(@LogToFile, fl);  Log('File Log');  ReadLn;end. ---------------------------------------------------------------------- alpine · 2022-07-09 17:17 Quote from: Warfley on July 08, 2022, 08:35:41 pmTo give a more practical example about this, with such a feature you can generalize partial function application which can be used to specialize functions: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}uses  SysUtils; type  generic TBinaryProcedure = reference to procedure(const A: TParam1; const B: TParam2);  generic TUnaryProcedure = reference to procedure(const A: TParam1); generic function Partial(Func: specialize TBinaryProcedure; const AValue: TParam1): specialize TUnaryProcedure;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;  fl: THandle;begin  // Log to consone out  Log := specialize Partial(@LogToFile, StdOutputHandle);  Log('Console Log');  // Log to console error  Log := specialize Partial(@LogToFile, StdErrorHandle);  Log('Error Log');  // Log to file  fl := FileOpen('log.txt', fmOpenWrite);  Log := specialize Partial(@LogToFile, fl);  Log('File Log');  ReadLn;end. Weird. I suppose that is the result of the effort for achieving a partial specialization overcoming limitation of the current generics in FPC, considering this and this. I'd use a generic advanced record for that particular case, just to keep the context (the handle) and to be of managed type. The latter is also not mandatory if you have at hand something like TAuto.  I'm a big fan of the functional programming too and I'm seeing the anonymous functions handy for map/fold/filter on containers and also for async/await futures (combined with a STAX?) but I'm not so sure for the function references.  Maybe they're good for replacing the current method delegates, but that would break the LCL backward compatibility, i.e. it would not happen. ---------------------------------------------------------------------- Warfley · 2022-07-09 20:34 Quote from: y.ivanov on July 09, 2022, 07:17:27 pmWeird. I suppose that is the result of the effort for achieving a partial specialization overcoming limitation of the current generics in FPC, considering this and this.This is the partial application pattern, that is used in function programming languages (https://wiki.haskell.org/Partial_application). You can see it as a generalization of OOP methods. Take for example this Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TTest = class  public procedure Foo (A: Integer);end; procedure TTest.Foo(A: Integer);... // method pointervar  MethodPtr: reference to procedure(A: Integer);begin  MethodPtr := @MyTest.Foo; The Foo method is then just a function that takes a hidden Self parameter, and the taking of the pointer to that function can be thought of creating a new function that fixes the first parameter to the value MyTest. So this is generally the same as: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TTestFoo(Self: TTest; A: Integer); var  MethodPtr: reference to procedure(A: Integer);begin  MethodPtr := procedure(A: Integer) begin TTestFoo(MyTest, A) end;The function partial just abstracts this process and might some day with implicit specialization work as easy as simply writing: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   MethodPtr := Partial(@TTestFoo, MyTest); So this is basically the generalization of OOP methods. And as such it allows to give really powerfull tools, because OOP methods are bound to classes (or objects) and inheritance. With partial application, the same principle can be extended to any type and any number of parameters. The example above, makes use of the polymorphism inherent in filehandles, to create one instance for each type, similar to inheriting from one base class multiple times, just for a single function (of course, such sets of functions could be combined, e.g. in a record, which would basically emulate classes and virtual methods). Together with generics, where as you stated this could be used as partial specialization, the same function could be used to be specialized for different types, where only the interface of the type must be the same. This allows to write basically inheritance like methods but for any types that do not need to be related (or even classes at all), as long as they either have a common interface (like a shared method) or there exist common overloaded functions for this type. If you go completely mad you could even completely emulate OOP with it: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+}{$ModeSwitch anonymousfunctions}{$ModeSwitch functionreferences}{$ModeSwitch nestedprocvars} uses  SysUtils; type  TLogFunction = reference to procedure(const AMessage: String);  TLogLevel = (llDebug=0, llWarning, llError, llNone);   TLogger = record          Debug: TLogFunction;    Warning: TLogFunction;    Error: TLogFunction;  end; function CreateLogger(AFile: THandle; LogLevel: TLogLevel): TLogger;   procedure NoLog(const AMessage: String);  begin end;   procedure LogMessage(const Prefix: String; const AMessage: String);  var    LogMessage: String;  begin    LogMessage := '%s: [%s] %s%s'.Format([Prefix, DateTimeToStr(Now), AMessage, LineEnding]);    FileWrite(AFile, LogMessage[1], LogMessage.Length);  end; begin  if LogLevel <= llDebug then    Result.Debug := procedure(const AMessage: String) begin LogMessage('DEBUG', AMessage) end  else    Result.Debug := @NoLog;   if LogLevel <= llWarning then    Result.Warning := procedure(const AMessage: String) begin LogMessage('WARNING', AMessage) end  else    Result.Warning := @NoLog;        if LogLevel <= llError then    Result.Error := procedure(const AMessage: String) begin LogMessage('ERROR', AMessage) end  else    Result.Error := @NoLog;end; function ConsoleLogger(LogLevel: TLogLevel): TLogger;begin  Result := CreateLogger(StdOutputHandle, LogLevel);end;   function ErrorLogger(LogLevel: TLogLevel): TLogger;begin  Result := CreateLogger(StdErrorHandle, LogLevel);end; var  Logger: TLogger;begin  Logger := ConsoleLogger(llDebug);  Logger.Debug('Debug Test');  Logger.Warning('Warning Test');  Logger.Error('Error Test');   Logger := ErrorLogger(llError);  Logger.Debug('Debug Test');  Logger.Warning('Warning Test');  Logger.Error('Error Test');  ReadLn;end. This has a really interesting property, that is that all the logging functions make use of the Handle, so it is like a private field in a class, but because it is a paramter, as soon as it goes out of scope, you can't access it from outside. It's basically the "purest" form of a private identifier, one that doesn't exist after the declaration. Aside from that, what I find really interesting about this approach, this is possible with only very few lines of code (basically the whole logger core type, which would be some form of abstract class in OOP, is with all function definitions just 40 lines of code). OOP introduces a lot of boilerplate code. This on the other hand is very slim. So I could imagine using this for some rather small things, where previously I would have used classes with just 1 or 2 (virtual/overriden) functions QuoteI'm a big fan of the functional programming too and I'm seeing the anonymous functions handy for map/fold/filter on containers and also for async/await futures (combined with a STAX?) but I'm not so sure for the function references.  Maybe they're good for replacing the current method delegates, but that would break the LCL backward compatibility, i.e. it would not happen. Function references have a huge andvantage. So I am writing a lot of code that makes use of function pointers, for example STAX, but also my iterators library (https://github.com/Warfley/ObjPasUtils/tree/master/src/iterators), and because I don't want to restrict the user to only one kind of function pointer, I need to define everything multiple times. Look at my functypes unit (https://github.com/Warfley/ObjPasUtils/blob/master/ src/functypes/functypes.pas): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   generic TFunction = function(): TResult;  generic TFunctionMethod = function(): TResult of object;  generic TFunctionNested = function(): TResult is nested;   generic TAnyFunction = record  public type    TMyType = specialize TAnyFunction;    TMyFunction = specialize TFunction;    TMyFunctionMethod = specialize TFunctionMethod;    TMyFunctionNested = specialize TFunctionNested;   public    class operator :=(AFunc: TMyFunction): TMyType; inline; overload;    class operator :=(AFunc: TMyFunctionMethod): TMyType; inline; overload;    class operator :=(AFunc: TMyFunctionNested): TMyType; inline; overload;   public    function apply(): TResult; inline;   private    case FunctionType: TFunctionType of      ftFunction: (FFunction: TMyFunction);      ftFunctionMethod: (FFunctionMethod: TMyFunctionMethod);      ftFunctionNested: (FFunctionNested: TMyFunctionNested);  end; class operator TAnyFunction.:=(AFunc: TMyFunction): TMyType;begin  Result.FunctionType := ftFunction;  Result.FFunction := AFunc;end; class operator TAnyFunction.:=(AFunc: TMyFunctionMethod): TMyType;begin  Result.FunctionType := ftFunctionMethod;  Result.FFunctionMethod := AFunc;end; class operator TAnyFunction.:=(AFunc: TMyFunctionNested): TMyType;begin  Result.FunctionType := ftFunctionNested;  Result.FFunctionNested := AFunc;end; function TAnyFunction.apply(): TResult;begin  case FunctionType of  ftFunction: Result := FFunction();  ftFunctionMethod: Result := FFunctionMethod();  ftFunctionNested: Result := FFunctionNested();  end;end;60 lines or so, just to basically implement something that behaves as function references (just worse, because implicit specializations don't work transitively through implicit casts). And I have this for all kinds of functions and procedures from 0 to 5 parameters, It's 600 lines of code, just to emulate what is now provided at language level (and better). Function references are a huge update, personally I feel they are a bigger upgrade than anonymous functions (as these where previously also possible using nested procedures). And somewhen in the future I will update both STAX and ObjPasUtils for the new features, this is going to give it a huge upgrade in usability ---------------------------------------------------------------------- alpine · 2022-07-10 10:45 Quote from: Warfley on July 09, 2022, 10:34:30 pmQuote from: y.ivanov on July 09, 2022, 07:17:27 pmWeird. I suppose that is the result of the effort for achieving a partial specialization overcoming limitation of the current generics in FPC, considering this and this.This is the partial application pattern, that is used in function programming languages (https://wiki.haskell.org/Partial_application). *snip* I see. Not familiar with Haskell but with the scheme language where the same pattern is used to its fullest. Quote from: Warfley on July 09, 2022, 10:34:30 pm*snip* So this is basically the generalization of OOP methods. And as such it allows to give really powerfull tools, because OOP methods are bound to classes (or objects) and inheritance. With partial application, the same principle can be extended to any type and any number of parameters. *snip*I don't see it as a generalization but as an alternative way to bind code to a context. I'll admit that in the latter you'll have an internally managed context, yes. Quote from: Warfley on July 09, 2022, 10:34:30 pm*snip* Aside from that, what I find really interesting about this approach, this is possible with only very few lines of code (basically the whole logger core type, which would be some form of abstract class in OOP, is with all function definitions just 40 lines of code). OOP introduces a lot of boilerplate code. This on the other hand is very slim. So I could imagine using this for some rather small things, where previously I would have used classes with just 1 or 2 (virtual/overriden) functions QuoteI'm a big fan of the functional programming too and I'm seeing the anonymous functions handy for map/fold/filter on containers and also for async/await futures (combined with a STAX?) but I'm not so sure for the function references.  Maybe they're good for replacing the current method delegates, but that would break the LCL backward compatibility, i.e. it would not happen. Function references have a huge andvantage. So I am writing a lot of code that makes use of function pointers, for example STAX, but also my iterators library (https://github.com/Warfley/ObjPasUtils/tree/master/src/iterators), and because I don't want to restrict the user to only one kind of function pointer, I need to define everything multiple times. Look at my functypes unit (https://github.com/Warfley/ObjPasUtils/blob/master/ src/functypes/functypes.pas): *snip* *snip* 60 lines or so, just to basically implement something that behaves as function references (just worse, because implicit specializations don't work transitively through implicit casts). And I have this for all kinds of functions and procedures from 0 to 5 parameters, It's 600 lines of code, just to emulate what is now provided at language level (and better). *snip* It reminds me of similar efforts in another language before the variadic templates were added. Anyway, it doesn't look any less weird to me.    IMHO Such an undertaking to approximate a Pascal so close to a functional language is a bit too much. But it is not that I don't appreciate it from the academic point of view. ---------------------------------------------------------------------- Warfley · 2022-07-10 14:20 Quote from: y.ivanov on July 10, 2022, 12:45:56 pmIMHO Such an undertaking to approximate a Pascal so close to a functional language is a bit too much. But it is not that I don't appreciate it from the academic point of view. I think that it is not necessarily trying to approximate pascal to a functional language, I think this is neither wise nor actually that possible (especially as the core concepts of functional languages, algebraic type system, fully expression based programs and side effect free programming, are something that must be supported on the very core of the language). That said, there are some concepts that are helpful in a cross paradigm way, e.g. most of the work in my ObjPasUtils is actually just creating such iterator functionality like map, filter, etc. for abritrary types, which I think is something that would be really helpful (there are libraries like LGenerics that provide these functions for their own types, but what I try to build is something that can be applied accross types to work with everything from arrays over TList to any custom class). This at it's core is not really functional programming (java has it for goodness sake, and I think you don't get further away from functional programming than with java), just because it uses function pointers. It's just something that will now be much easier. That said, there are also a lot of more academic, the OOP emulation shown above, is basically the concept of prototype based OOP as used in Javascript or Python, and I think can be really helpful in some scenarios (but is not a cureal for every use-case, I think this gets unreadable if the size of the class is larger than 1-2 functions). So how useful this will be is to be shown. And then there are other things like implementing monads, which could be very interesting from an academic point of view, but will be stretching the language probably to a breaking point (I want to see if I can basically create something like the monadic chaining operator for OOP, where where you could write something like csv := Chain(TStringList.Add, TStringList.Add, TStringList.SetDelimiter, TStringList.GetDelimitedText)(TStringList.Create, 'Item1', 'Item2', ','). But I like to think that at least the stuff already implemented in ObjPasUtils is useful and not just an academic exercise. ---------------------------------------------------------------------- Peter H · 2022-08-27 18:20 Hi, I noticed this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     procedure(const aArg: String)    begin         Writeln(aArg);    end('Hello World');  is not supported by codetools. It says: project1.lpr(26,5) Error: unexpected keyword "procedure" Also this compiles, but is not accepted by codetools: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i p:= function foo:integer      begin         foo:= 4711;      end;  It does accept this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i i:= function:integer      begin         Result:= 4711;      end();  Just for the records, it is not an important problem. Edit: Compilable and runnable example attached. ---------------------------------------------------------------------- PascalDragon · 2022-08-28 16:07 Best report it as an issue for Lazarus then. ---------------------------------------------------------------------- Peter H · 2022-08-29 02:26 I have done this now, was a lot of work create account. GitLab is not for beginners and casual programmers like me. I put the code in, cleanly formatted as seen here, and when I submitted it formatting was destroyed. Then you have to read several pages about GitLab Markdown language before you learn how to fix this. https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/39871 ---------------------------------------------------------------------- lazycat2 · 2022-12-07 09:11 Looks like I'm missing something obvious (I'm new to FPC) but where how do I get support for those new features? The FPC 3.2.2 doesn't seem to have it and there's no download for a newer version. ---------------------------------------------------------------------- dbannon · 2022-12-07 09:33 Well, safest and easy to wait for the next release. But if you want to have it now, build it from source. Your existing 3.2.2 will build it. See https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler Down load a zip ball or using git .... Keep it in a separate directory to your existing install and set PATH accordingly. Davo ---------------------------------------------------------------------- Peter H · 2022-12-07 11:32 Quote from: lazycat2 on December 07, 2022, 10:11:44 amLooks like I'm missing something obvious (I'm new to FPC) but where how do I get support for those new features? The FPC 3.2.2 doesn't seem to have it and there's no download for a newer version. If you mainly are interested in new features, install Lazarus and Freepascal using fpcupdeluxe and install the trunk version. You can then install multiple versions of lazarus and fpc side by side. Or install Codetyphon for experimental purposes. They are always based on trunk. BTW, I tested this now months later and the bug with codetools is fixed, thanks to the team! ---------------------------------------------------------------------- dbannon · 2022-12-07 12:12 Peter, please look at this thread - https://forum.lazarus.freepascal.o rg/index.php/topic,61479.msg462951.html#msg462951 Note that I posted a very long winded message 37 minutes previously. In that 37 minutes, I cloned a Debian Bullseye VM, installed the repo FPC, downloaded the Lazarus Src and built a fully functional Lazarus. We don't need no Code Typhoon when building Lazarus is that easy ! Davo ---------------------------------------------------------------------- lazycat2 · 2022-12-08 18:51 Quote from: dbannon on December 07, 2022, 10:33:55 amWell, safest and easy to wait for the next release. But if you want to have it now, build it from source. Your existing 3.2.2 will build it. See https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler Thanks. I had a suspicion that I have to build it - now it's confirmed ---------------------------------------------------------------------- Peter H · 2022-12-11 16:39 Quote from: lazycat2 on December 08, 2022, 07:51:47 pmQuote from: dbannon on December 07, 2022, 10:33:55 amWell, safest and easy to wait for the next release. But if you want to have it now, build it from source. Your existing 3.2.2 will build it. See https://wiki.freepascal.org/Installing_the_Free_Pascal_Compiler Thanks. I had a suspicion that I have to build it - now it's confirmed If you use fpcupdeluxe it will build it for you and download everything required. ---------------------------------------------------------------------- mr-highball · 2022-12-17 04:59 No idea how I missed this announcement back in May but this is great! Looking forward to slinging some anon functions around! ---------------------------------------------------------------------- flowCRANE · 2023-06-05 17:46 In the first post of this thread, the following demo is provided: Quote from: PascalDragon on May 26, 2022, 09:47:14 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFunc = function: LongInt; var  p: TProcedure;  f: TFunc;  n: TNotifyEvent;begin  procedure(const aArg: String)  begin    Writeln(aArg);  end('Hello World');    p := procedure       begin           Writeln('Foobar');           end;  p();    n := procedure(aSender: TObject);       begin             Writeln(HexStr(Pointer(aSender));           end;  n(Nil);    f := function MyRes : LongInt;       begin             MyRes := 42;         end;  Writeln(f());end. Can somebody explain me what the hell is this highlighted monster? I'm betting that the final end is simply given a list of parameters so that this anonymous function is called with them. That is, it is a declaration of an anonymous function with its simultaneous call. Correct? What is the practical use of something like this? Why would it make sense to separate a piece of code for such a function? By the way, thanks for the anonymous functions — I've been missing them for a long time. Nice to see them! ---------------------------------------------------------------------- PascalDragon · 2023-06-05 19:25 Quote from: furious programming on June 05, 2023, 07:46:48 pmI'm betting that the final end is simply given a list of parameters so that this anonymous function is called with them. That is, it is a declaration of an anonymous function with its simultaneous call. Correct? What is the practical use of something like this? Why would it make sense to separate a piece of code for such a function? It's indeed the declaration of an anonymous function with a direct call of it. There isn't much use for this explicit functionality, but it shows that anonymous functions by themselves are simply valid expressions that can be called or assigned to function pointers, method pointers, nested function pointers or function references. ---------------------------------------------------------------------- runewalsh · 2023-06-05 19:54 Quote from: PascalDragon on June 05, 2023, 09:25:00 pmThere isn't much use for this explicit functionality This is a workaround for not having inline variables! :D ---------------------------------------------------------------------- PascalDragon · 2023-06-05 20:00 Quote from: runewalsh on June 05, 2023, 09:54:40 pmQuote from: PascalDragon on June 05, 2023, 09:25:00 pmThere isn't much use for this explicit functionality This is a workaround for not having inline variables! Much more verbose than just scrolling up and declaring the variable there...  🙄 ---------------------------------------------------------------------- Peter H · 2023-06-05 23:07 Can possibly been used to capture variables by value: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  var  i,j,k:integer; ...........begin........ procedure(i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end(i,j,k);  inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value  I did not test if this really works.... ---------------------------------------------------------------------- Peter H · 2023-06-06 07:09 I tested it. I copied this anonymous procedure into a thread and called it in a loop. It captures variables by value and they are printed correctly in the main thread, even if I introduced a time delay. I also printed the addresses of the captured variable and at each iteration it gets a new adress. So this seems to work. I cannot say if it is ready for regular use, but in my experiment it worked. Here is a helpful video from Bojan Mitov about this. But he uses much more complicated ways to achieve the same goal. https://youtu.be/HDhmUjzUNyQ?t=1918 ---------------------------------------------------------------------- Bad Sector · 2023-06-06 14:11 I just noticed this and anonymous functions look very useful :-). However i tried it with my 3D engine to replace some sorting comparison callbacks with anonymous functions but i get an internal error 20020475. The code i try to compile is: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       // LLightRows.Sort(@LightRowSorter, Self);      LLightRows.Sort(function(const A, B: TLightRow): SizeInt // <- i get the internal error here        var          AP, BP: TVector;          SA, SB: Single;        begin          AP:=ViewMatrix.Transformed(PVector(@A)^);          ProjectionMatrix.TransformProj(AP);          BP:=ViewMatrix.Transformed(PVector(@B)^);          ProjectionMatrix.TransformProj(BP);          SA:=AP.Z*10000 + AP.X;        SB:=BP.Z*10000 + BP.X;          if SA < SB then Exit(-1);        if SA > SB then Exit(1);          Result:=0;        end      );  The internal error is set right after "SizeInt". If it may help give some additional context: The commented out line was the previous call that passed "Self" in the comparison function so i could access the ViewMatrix and ProjectionMatrix fields - this is called by a method) LLightRows is a "specialize TDynArray" and TDynArray is a generic container: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   generic TDynArray = object(specialize TDynArrayView)  type    // Callback function for sorting    TSorterWithData = function(const A, B: T; Data: Pointer): SizeInt;    TSorter = function(const A, B: T): SizeInt;   ...     // Sort the array using the given sorting function with the passed data    procedure Sort(ASorter: TSorterWithData; AData: Pointer);    // Sort the array using the given sorting function    procedure Sort(ASorter: TSorter);  (the "with data" is the original version that used a plain callback with a data pointer that was used to access whatever context needed, the second one - without the "with data" part - is the exact same thing just removing the "data" parameter since any necessary data could be passed at call time) I use this in about a dozen places with pretty much all sort callbacks being 4-5 lines of code and it'd be neat if i could replace those with anonymous functions. This is 67eac52f0754324e72def975fbfa4d18db27afc1 - i.e. git main from last week, but a quick look in the commit log doesn't show anything relevant so i'd expect the error to still be there. ---------------------------------------------------------------------- PascalDragon · 2023-06-06 19:55 Quote from: Peter H on June 06, 2023, 01:07:55 amCan possibly been used to capture variables by value: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  var  i,j,k:integer; ...........begin........ procedure(i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end(i,j,k);  inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value  I did not test if this really works.... This is the same as Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  procedure NestedFunc(i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end;   var  i,j,k:integer; ...........begin........ NestedFunc(i,j,k); inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value  Which is the same as the following: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  i,j,k:integer; ...........begin........ Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value  So the outer anonymous function is essentially useless. Quote from: Bad Sector on June 06, 2023, 04:11:36 pmI just noticed this and anonymous functions look very useful :-). However i tried it with my 3D engine to replace some sorting comparison callbacks with anonymous functions but i get an internal error 20020475. The code i try to compile is: [ … ] I use this in about a dozen places with pretty much all sort callbacks being 4-5 lines of code and it'd be neat if i could replace those with anonymous functions. This is 67eac52f0754324e72def975fbfa4d18db27afc1 - i.e. git main from last week, but a quick look in the commit log doesn't show anything relevant so i'd expect the error to still be there. Please report a bug with a small, self contained, reproducible example. ---------------------------------------------------------------------- Peter H · 2023-06-06 21:17 Quote from: PascalDragon on June 06, 2023, 09:55:50 pmSo the outer anonymous function is essentially useless. I do not think so. This would be useless: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure(var i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end(i,j,k); inc(i);....... This captures the variables by reference. Lets assume i is zero. The inner procedure is queued (not synchronized), this means the inner procedure is executed in the main thread. It will wait for 500ms, but the child thread will go on without delay. If the thread increments i after 250 ms, this routine will print i as 1. This routine captures the values, not the references. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure(i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end(i,j,k); inc(i);....... If i is incremented after 250ms this procedure will still print 0 and not 1, because it captures the momentary values of the variables and not the addresses of the variables. My experiments have shown this, I tested it. Please consider, the outer procedure is executed in the context of the (child) thread, but the inner procedure is executed asynchronously and delayed in the context of the main GUI thread. It is however better to use not the same shadowing variable names in the anonymous procedure, because this confuses the debugger apparently. It will always display the outer variable, not the inner. (The compiler is not confused about this, but the debugger is) So this is better, but has the same functionality: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure(i2,j2,k2:integer)begin    Queue(procedure begin Sleep(500); Writeln(i2,' ',hexstr(@i2),' ',j2,' ',k2);end);end(i,j,k); BTW, Delphi does not support this construct. Due to inline variables and constants and type inference you can write this in Delphi: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i const pp= procedure(i2,j2,k2:integer)begin    Queue(procedure begin Sleep(500); Writeln(i2,' ',hexstr(@i2),' ',j2,' ',k2);end);end;pp(i,j,k);  I have yet to find out if it can capture by value in the same way. This is very useful if a anonymous procedure is called with time delay or from another thread. ---------------------------------------------------------------------- Peter H · 2023-06-07 13:31 Quote from: PascalDragon on June 06, 2023, 09:55:50 pmSo the outer anonymous function is essentially useless. Just try this, it is Delphi code and Free Pascal behaves in the same way... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  Tpr = reference to procedure;var   p_inner: Tpr;      str: String;begin     str := 'Line2: Hello World!';     const p_outer = procedure(s:String)    begin      p_inner := procedure      begin         Writeln(s);      end;    end;    p_outer(str);  //This line prints nothing,           //it just instantiates p_outer and p_inner and captures their local context.    str := '--------------This is NOT printed!----------------';     Writeln('Line1: p_outer was called, now calling p_inner');    p_inner();  //This line prints the string s="Line2...", which was captured by p_outer.     readln;end.   The output is this: Code: [Select]Line1: p_outer was called, now calling p_inner Line2: Hello World! Edit: I tested in FPC: Lazarus 2.3.0 (rev 0c96c0e3a5) FPC 3.3.1 x86_64-win64-win32/win64 Unfortunately it does not run, it raises an access violation. The problem is line 12:  Writeln(s);, if I comment it out, it runs. When I use integer instead of String, it doesnt crash but prints an invalid number. So there are still some fundamental problems with anonymous procedures. Just a remark: The word anonymous is somewhat misleading. Anonymous functions do not need a name, because there is - and always must be - a named or indexed reference pointing to them. This is my Freepascal example: It MUST print "1", but it prints a random number. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  type  Tpr = reference to procedure;var   p_inner: Tpr;      str: integer;begin     str := 1;     procedure(s:integer)    begin      p_inner := procedure      begin         Writeln(s);      end;    end(str);    //p_outer(str);  //This line prints nothing,           //it just instantiates p_outer and p_inner and captures their local context.    str := 2;//'--------------This is NOT printed!----------------';     Writeln('Line1: p_outer was called, now calling p_inner');    p_inner();  //This line prints the string s="Line2...", which was captured by p_outer.     readln;end. ---------------------------------------------------------------------- Peter H · 2023-06-08 14:02 This works in Free Pascal: The point is, (unlike Delphi) it doesnt work, if the anonymous procedures are coded inside the initial Code: [Select]begin end. It gives access violation or random results, depending on the code. It must be coded inside a procedure. Then it works as intended. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}// {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  Tpr = reference to procedure;var   p_inner: Tpr; procedure caller;begin  p_inner();end; procedure main; var str: String;begin     str := 'Hello World!';     p_inner := function(s:String):Tpr //This captures local and persistent copy of "str"    begin      Result := procedure      begin         Writeln(s);      end;      Writeln('Outer function was called');    end(str);  //This does NOT print the string "s"!             //it just instantiates the outer function and p_inner and captures their local context.    SetLength(str,0); //Erase the string content     Writeln('now calling p_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.               //p_inner will be called from an external context, just for test and demonstrationend; begin  main;  p_inner(); //calling p_inner again, when the context of main() is destroyed  readln;end.  This is just a nontrivial  example for learning and testing purposes. The power of lambdas and closures becomes obvious, it you consider, "p_inner" could be called from other units and even from other threads via Synchronize() or via Queue(). ---------------------------------------------------------------------- Thaddy · 2023-06-08 14:23 the type can be outside the procedure.. ---------------------------------------------------------------------- Peter H · 2023-06-08 14:38 Quote from: Thaddy on June 08, 2023, 04:23:38 pmthe type can be outside the procedure.. Yes, compatible types are globally predefined in LCL. But for this test I preferred to define everything myself, just for clarity, so it is clear to see what I did, without looking up the LCL definitions. Edit: I accept your proposal. However this is just test code, therefore I did not consider this. I changed the type definition, so "p_inner" can be called from other procedures. ---------------------------------------------------------------------- PascalDragon · 2023-06-08 16:07 Quote from: Peter H on June 06, 2023, 11:17:10 pmQuote from: PascalDragon on June 06, 2023, 09:55:50 pmSo the outer anonymous function is essentially useless. I do not think so. This would be useless: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure(var i,j,k:integer)begin    Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);end(i,j,k); inc(i);....... This captures the variables by reference. Right, I hadn't noticed that this essentially captured by value... Quote from: Peter H on June 07, 2023, 03:31:58 pmEdit: I tested in FPC: Lazarus 2.3.0 (rev 0c96c0e3a5) FPC 3.3.1 x86_64-win64-win32/win64 Unfortunately it does not run, it raises an access violation. The problem is line 12:  Writeln(s);, if I comment it out, it runs. When I use integer instead of String, it doesnt crash but prints an invalid number. So there are still some fundamental problems with anonymous procedures. Please report a bug with a reproducible example. Quote from: Peter H on June 07, 2023, 03:31:58 pmJust a remark: The word anonymous is somewhat misleading. Anonymous functions do not need a name, because there is - and always must be - a named or indexed reference pointing to them. In both Delphi and FPC the anonymous refers to the fact that the function body has no name, no matter if you assign it to a variable or pass it as a parameter to another function. Quote from: Peter H on June 07, 2023, 03:31:58 pmThis is my Freepascal example: It MUST print "1", but it prints a random number. Please report a bug for this as well. ---------------------------------------------------------------------- Peter H · 2023-06-08 21:58 Quote from: PascalDragon on June 08, 2023, 06:07:42 pmQuote from: Peter H on June 07, 2023, 03:31:58 pmThis is my Freepascal example: It MUST print "1", but it prints a random number. Please report a bug for this as well. The two bugs are the same. When I use integers it gives random results, when I use strings it gives access violations, because strings under the hood use pointers. I assume the developers already know this, because this feature, anonymous functions, and the trunk compiler are work in progress and heavily tested. ---------------------------------------------------------------------- Peter H · 2023-06-08 23:00 I have tried to write a bug report, but do not understand this markup code I must use. (I could write it in TeX, but do not understand this markup code ;-) If I type a line feed in the editor, then when I activate the preview, all linefeeds are gone. When I insert source code I get errormessages. It is much easier to write here, so I ask someone else to report it. This code compiles and works ok: It outputs 4 lines of text. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}// {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  Tpr = reference to procedure;var   p_inner: Tpr; procedure caller;begin  p_inner();end; procedure main; var str: String;begin     str := 'Hello World!';     p_inner := function(s:String):Tpr //This captures local and persistent copy of "str"    begin      Result := procedure      begin         Writeln(s);      end;      Writeln('Outer function was called');    end(str);  //This does NOT print the string "s"!             //it just instantiates the outer function and p_inner and captures their local context.    SetLength(str,0); //Erase the string content     Writeln('now calling p_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.               //p_inner will be called from an external context, just for test and demonstrationend; begin  main;  p_inner(); //calling p_inner again, when the context of main() is destroyed  readln;end.  If I modify the code of main() so that it is between begin and end. (no main procedure) then it compiles, but gives access violation when it runs: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}// {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  Tpr = reference to procedure;var   p_inner: Tpr; procedure caller;begin  p_inner();end; //procedure main; var str: String;begin     str := 'Hello World!';     p_inner := function(s:String):Tpr //This captures local and persistent copy of "str"    begin      Result := procedure    begin         Writeln(s);      end;      Writeln('Outer function was called');    end(str);  //This does NOT print the string "s"!             //it just instantiates the outer function and p_inner and captures their local context.    SetLength(str,0); //Erase the string content     Writeln('now calling p_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.               //p_inner will be called from an external context, just for test and demonstrationend. ---------------------------------------------------------------------- Marc · 2023-06-09 07:06 To write a block of code in gitlab (the bugtracker), start your block of code with three ``` and end your block with three ``` like: Code: [Select]``` procedure MyProc; begin end; ``` ---------------------------------------------------------------------- Thaddy · 2023-06-09 08:37 Yese, three back ticks, note you get pascal formatted code if you do it like this: ```pascal /// your code ``` ---------------------------------------------------------------------- Peter H · 2023-06-09 08:55 @Marc, Thaddy, thank you! How can I know this without reading documentation for hours? To insert source code, I clicked the symbol in the editor and this creates only two ticks. And when I then view it in the preview I see a bunch of error messages. Anyway, I believe it is too early to report errors about this feature because I would not find an end. The problems are obvious if you compile or run nontrivial examples, which run without problems in Delphi. I modified the source, so that it compiles unchanged without problems in Delphi. Compiler version is: Lazarus 2.3.0 (rev 0c96c0e3a5) FPC 3.3.1 x86_64-win64-win32/win64 I do however believe, this is a general problem not specific to windows. So more people should test this on other OS before reporting bugs. (I have no Linux currently) And I am almost 70 years old and retired so I will stay with windows, because I need it for my other stuff. The following source code gives access violation at runtime in FPC. If I modify the source and declare "f_outer" local, then it gives internal compiler error in FPC. Both versions compile and run ok in Delphi. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}// {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized" uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  Tproc = reference to procedure;      Tfunc = reference to function(s:String):Tproc;var   p_inner: Tproc;      f_outer: Tfunc; procedure caller;begin  p_inner();end; procedure main; var str: String;//    f_outer: Tfunc;    // <-----  doesnt compile when this is uncommented begin     str := 'Hello World!';     f_outer := function(s:String):Tproc //This captures local and persistent copy of "str"    begin      Result := procedure  //  <----Access violation at runtime here in FPC, but not in Delphi. Program runs ok in Delphi.    begin         Writeln(s);      end;      Writeln('Outer function was called');    end;    p_inner := f_outer(str);                  //This does NOT print the string "s"!               //it just instantiates the outer function and p_inner and captures their local context.    SetLength(str,0); //Erase the string content     Writeln('now calling p_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.               //p_inner will be called from an external context, just for test and demonstrationend; begin  main;  p_inner(); //calling p_inner again, when the context of main() is destroyed  readln;end. ---------------------------------------------------------------------- Thaddy · 2023-06-09 16:07 It can be just a name clash since in freepascal Tproc and Tfunc are already defined. Try it with different names. ---------------------------------------------------------------------- PascalDragon · 2023-06-09 20:11 Quote from: Peter H on June 08, 2023, 11:58:02 pmThe two bugs are the same. When I use integers it gives random results, when I use strings it gives access violations, because strings under the hood use pointers. Ah, right, somehow I had read the first one as triggering an internal error instead of an access violation Quote from: Peter H on June 08, 2023, 11:58:02 pmI assume the developers already know this, because this feature, anonymous functions, and the trunk compiler are work in progress and heavily tested. I am one of those developers. Quote from: Peter H on June 09, 2023, 10:55:02 amAnyway, I believe it is too early to report errors about this feature because I would not find an end. The problems are obvious if you compile or run nontrivial examples, which run without problems in Delphi. No, it's not too early, because I explicitly requested users to test this in the initial post, because the tests that I do have do work correctly and we need to find those cases that don't: Quote from: PascalDragon on May 26, 2022, 09:47:14 pmAs these two features are rather complicated there might still be a huge bundle of bugs lurking around so I ask you to test them to year heart's content and report found bugs to the issues on GitLab so that we can fix as many of them as possible before the next major version (which is not yet planned, so don't worry ). Quote from: Peter H on June 09, 2023, 10:55:02 amThe following source code gives access violation at runtime in FPC. If I modify the source and declare "f_outer" local, then it gives internal compiler error in FPC. Both versions compile and run ok in Delphi. Please report once for the access violation / random data and once for the internal error. ---------------------------------------------------------------------- Peter H · 2023-06-10 12:33 Ok thank you. I do however think, as long as there are compiler problems it doesnt make sense to report all runtime problems. I changed my project a little bit, I converted the procedure into a function, which returns a string. Now I get compiler errors only. The program compiles and runs in Delphi. Also this cumbersome bunch of confusing type declarations is only necessary due to the lack of type inference in FPC. And I do not see how type inference could be possible without inline variables. Look for line 37. Then, if I uncomment line 27, I get different errors. So I could report problems without end, but I do not want to do this, it makes no sense. So I believe, runtime errors are irrelevant as long as the compiler has obvious problems. It would be too confusing to report every and each runtime error, because these are secondary to compiler errors. They will hopefully automagically vanish if the compiler problems are fixed. (I do not want to warm up annoying discussions about inline variables. If there are other solutions, I am open for it) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1;{$ifdef FPC}{$mode objfpc}{$H+}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}// {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized"{$endif}uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  TfuncS = reference to function:String;      TfuncF = reference to function(s:String):TfuncS;var   f_inner: TfuncS;      f_outer: TfuncF; procedure caller;begin  f_inner();end; procedure main; var str: String;   // f_outer: TfuncF;    // <----doesnt compile in FPC when this is uncommented, but compiles and runs ok in Delphi begin   str := 'Hello World!';     f_outer := function(s:String):TfuncS //This captures local and persistent copy of "str"    begin      Result := function:String      begin        Result:=s;  // <---- project1.lpr(37,9) Error: Internal error 2011010304        Writeln(s);      end;      Writeln('Outer function was called');    end;    f_inner := f_outer(str);   //This instantiates the outer function and f_inner and captures their local context.     SetLength(str,0); //Erase the string content     Writeln('now calling f_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.               //f_inner will be called from an external context, this is just for test and demonstrationend; begin  main;  Writeln('Now the context of "main()" is lost. Can we still print the string "str"?');  f_inner();  readln;end. ---------------------------------------------------------------------- Peter H · 2023-06-10 21:48 I have now created a bug report. https://gitlab.com/freepascal.org/fpc/source/-/issues/40315 I must apologize, I cannot master this markup language. In the first (code) section linebreaks are ok. In the two following sections not, I do not know why. If I write a linebreak, I mean a linebreak and it should not remove it. How to achieve this? The "" symbol in the editor does not work here, maybe it is my browser? (Edge) It outputs only two backticks, but the documentation of the markup language says I must use three before the code section and three after. :-( The content of this bug report /should/ be like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1;//This program compiles and runs in Delphi and in FPC. (at least should run in FPC)//It is intentionally designed this way.{$ifdef FPC}  {$mode objfpc}{$H+}  {$modeswitch functionreferences}  {$modeswitch anonymousfunctions}  // {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized"{$endif}uses  {$IFDEF UNIX}  cthreads,  {$ENDIF}  Classes,Sysutils { you can add units after this }; type  TfuncS = reference to function:String;      TfuncF = reference to function(s:String):TfuncS;var   f_inner: TfuncS;      f_outer: TfuncF; procedure caller;begin  f_inner();end; procedure main; var str: String;   // f_outer: TfuncF;  // <---- doesnt compile in FPC when this is uncommented, but compiles and runs ok in Delphi begin   str := 'Hello World!';     f_outer := function(s:String):TfuncS //This captures local and persistent copy of "str"    begin      Result := function:String      begin        Result := s;  // <---- project1.lpr(37,9) Error: Internal error 2011010304                    // if the line is commented out it compiles, but gives access violation at runtime         Writeln(s);      end;      Writeln('Outer function was called');    end;    f_inner := f_outer(str);   //This instantiates the outer function and f_inner and captures their local context.     SetLength(str,0); //Erase the string content     Writeln('now calling f_inner');    caller();  //This line prints the string s="Hello World!", which was captured by the outer function.             //f_inner will be called from an external context, this is just for test and demonstrationend; begin  main;  Writeln('Now the context of "main()" is lost. Can we still print the string "str"?');  if f_inner()='Hello World!' then writeln('Yes! :-)') else writeln ('No! :-(');   readln;end.  However, beginning from here, all intentional linebreaks are lost in GitLab, and I do not know, why. Code: [Select] Compile Project, Target: C:\pasPRJ\AnonymousProcs\project1.exe: Exit code 1, Errors: 1, Hints: 2 Hint: Start of reading config file C:\fpc\fpc\bin\x86_64-win64\fpc.cfg Hint: End of reading config file C:\fpc\fpc\bin\x86_64-win64\fpc.cfg Verbose: Free Pascal Compiler version 3.3.1-12746-g9bfb45dc05 [2023/06/10] for x86_64 Verbose: Copyright (c) 1993-2023 by Florian Klaempfl and others Verbose: Target OS: Win64 for x64 Verbose: Compiling project1.lpr Progress: 2 233/768 Kb Used project1.lpr(39,9) Error: Internal error 2011010304 Verbose: C:\fpc\fpc\bin\x86_64-win64\ppcx64.exe returned an error exitcode Code: [Select] The output of the program, when run in Delphi is: Outer function was called now calling f_inner Hello World! Now the context of "main()" is lost. Can we still print the string "str"? Hello World! Yes! :-) ---------------------------------------------------------------------- Bad Sector · 2023-06-12 21:25 Quote from: PascalDragon on June 06, 2023, 09:55:50 pmPlease report a bug with a small, self contained, reproducible example. What is internal error 200204175 about? I tried to put the code in a single self-contained program but that compiles without the error. Perhaps it can give me some idea what exactly causes it so i can make a reproducible example. ---------------------------------------------------------------------- Peter H · 2023-06-12 22:06 So, if you did not get the error, I am happy and want to know which OS and compiler you use. And which exact source. I am then happy to compile again and compare. Please try my later examples, which are on GitLab. These are intentionally made to compile out of the box. Or, if they do not compile, they show errors out of the box. The sourcecode is selfcontaining and has no other  external depencies than "WriteLn". All compile on Delphi and deliver the expected result. Please ignore the older postings, I improved the informations and error reports inbetween. When I initially posted this, it was not totally clear to myself, but now at least the problem is clear to me. Just paste the source code into an  empty project, compile, and if possible, run. It should be a matter of seconds. https://gitlab.com/freepascal.org/fpc/source/-/issues/40315 https://gitlab.com/freepascal.org/fpc/source/-/issues/40316 ---------------------------------------------------------------------- duzenko · 2023-10-04 06:36 Hi, is it possible to use an anonymous function with TFPGList.Sort? (And specifically in {$mode delphi}, too) This does not compile: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    procedure FindClose;   var      CompareGroup: function(const Item1, Item2: PGroup): Integer;   begin      CompareGroup := function(a, b: PGroup): Integer;      begin         Result :=  0;      end;      ... ---------------------------------------------------------------------- bytebites · 2023-10-04 08:24 There is const missing. ---------------------------------------------------------------------- PascalDragon · 2023-10-05 19:10 Also you can't access local variables of the outer function in that case, because the Sort method does not take a function reference, but a function pointer and those can't take the state required. ---------------------------------------------------------------------- Mr.Madguy · 2023-11-10 09:21 Confusion about closures' functionality and implementation is caused by not understanding their purpose. Their purpose is to define classes without actually defining classes. To do things like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Example; type  TMyProc = reference to procedure;  TMyClass = record  MyInc:TMyProc;    MyDec:TMyProc;    MyPrint:TMyProc;  end; function MyConstructor:TMyClass;  var I:Integer;begin  I := 0;  Result.MyInc := procedure  begin    Inc(I);  end;  Result.MyDec := procedure  begin  Dec(I);  end;  Result.MyPrint := procedure  begin    WriteLn(I);  end;end; var  MyObject:TMyClass; begin  MyObject := MyConstructor;  MyObject.MyPrint();  MyObject.MyInc();  MyObject.MyPrint();  MyObject.MyDec();  MyObject.MyPrint();end. But as we don't need such functionality in Pascal, they're usually used to declare callbacks with arbitrary data attached to them. If we need to pass some data to callback, we usually need to mess with declaring some structure or class and passing pointer to it to callback: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TData = class    public      X, Y, Z:TSomeType;  end;   procedure AddCallback(AData:Pointer);  begin  end; var  Data:TData;  X, Y, Z:TSomeType; begin  Data := TData.Create;  Data.X := X;  Data.Y := Y;  Data.Z := Z;  SomeProcThatCallsCallback(AddCallback, pointer(Data));  Data.Free;end;  But things can be made much easier with closures: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  var  X, Y, Z:TSomeType;   SomeProcThatCallsCallback(    procedure    begin      //Just use X, Y and Z here!    end  ); Please note, that only global variables can be used in callbacks directly. But global variables aren't always usable. For example they shouldn't be used in thread- safe code. In this case local variables should be used instead. And they can't be used directly. Example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TList.FilteredCopy(AFilter:TFilter):TList;  var Temp:TList;begin  //As I remember, Result can't be captured, but I can be wrong.  Temp := TList.Create;  DoFilter(AFilter,    procedure(AItem:TItem)    begin    Temp.Add(AItem);    end  );  Result := Temp;end; ---------------------------------------------------------------------- Warfley · 2023-11-10 11:07 Quote from: Mr.Madguy on November 10, 2023, 10:21:19 amConfusion about closures' functionality and implementation is caused by not understanding their purpose. Their purpose is to define classes without actually defining classes. To do things like this: I would not put it this way, because closures exited long before the concept of Objects existed, the concept comes from Lambda Calculus from the 1930s and even the first implementation in programming languages was in the 60s, long before OOP became a thing. Rather it is that objects and closures basically try to archive the same goal, associating code with data into one first in class language entity, you can refer to in code, and apply logic and operations on. The main difference is that in Object the state management is explicit, in the sense that you must explicetly write a class definition containing all the data that will be associated with the functionality, while closures are implicit, where you just write your function, and the data that will be captured will be decided by the compiler from the context. This means generally that closures allow to have a smaller footprint in the code, but understanding those is much more context dependent, as you do not have one place where all the information is listed. So typically you would use closures for things which are quite simple and where the effort of writing a class would not add to the understanding, e.g. when it's just a function that is parameterized with very few data elements (e.g. a filter operation where you have a comparator), while usually for more complex thing, e.g. when you have more than one function associated with the data, you would use a class. ---------------------------------------------------------------------- amerou · 2023-12-04 21:18 Is there a timeline when this feature is going to be available? ---------------------------------------------------------------------- Thaddy · 2023-12-05 08:40 As written earlier in the thread, that you obviously did not read, it is already available in trunk. See the first message in this thread and the 7th message in this thread. ---------------------------------------------------------------------- Zoran · 2023-12-10 07:01 Quote from: Thaddy on December 05, 2023, 09:40:18 amAs written earlier in the thread, that you obviously did not read, it is already available in trunk. See the first message in this thread and the 7th message in this thread. Thaddy, by beeing available, one usually means in the latest released version. ---------------------------------------------------------------------- Thaddy · 2023-12-10 10:10 Well, Marco already pointed out that in that case assume 3.2.2 fixes because it seems the version is back ported from trunk after all, contrary to was first anounced in this thread. I assume you agree that 3.2.2 fixes can be considered some kind of release version. I usually consider a feature available after it is announced. ---------------------------------------------------------------------- Zoran · 2023-12-10 12:02 Quote from: Thaddy on December 10, 2023, 11:10:54 amI usually consider a feature available after it is announced. You can of course, as you can start using it immediately, as fpc development version is always available. However, you should not assume the development version to be stable for production code, so you must not assume it is available for everyone. It is quite usual to only use a "release" in production code. Furthermore, it is not so unusual that even when a new version is released, some don't upgrade immediately, but wait a while more to prove stability. Quote from: Thaddy on December 05, 2023, 09:40:18 amSee the first message in this thread and the 7th message in this thread. I do admit that the message you mentioned (which is 7th in the thread only if you count from zero) answers the question. ---------------------------------------------------------------------- PascalDragon · 2023-12-10 20:26 Quote from: Thaddy on December 10, 2023, 11:10:54 amWell, Marco already pointed out that in that case assume 3.2.2 fixes because it seems the version is back ported from trunk after all, contrary to was first anounced in this thread. I assume you agree that 3.2.2 fixes can be considered some kind of release version. This feature is not available in 3.2.2 or 3.2.3 and won't be. Don't know how you came to that conclusion... Quote from: Thaddy on December 10, 2023, 11:10:54 amI usually consider a feature available after it is announced. A feature is indeed considered available once it's announced, but not in a release, but in the development version. ---------------------------------------------------------------------- deadserious · 2024-01-16 21:25 Quote from: PascalDragon on December 10, 2023, 09:26:48 pmThis feature is not available in 3.2.2 or 3.2.3 and won't be. Don't know how you came to that conclusion... What would it take to get a 3.3.0 with this feature released? ---------------------------------------------------------------------- marcov · 2024-01-16 21:57 Eventually it will be released as 3.4.0, but the exact schedule for that is still unknown. ---------------------------------------------------------------------- deadserious · 2024-01-17 23:20 Quote from: marcov on January 16, 2024, 10:57:39 pmEventually it will be released as 3.4.0, but the exact schedule for that is still unknown. Is there a roadmap of fixes/features we're waiting on for 3.4? ---------------------------------------------------------------------- dsiders · 2024-01-17 23:45 Quote from: deadserious on January 18, 2024, 12:20:23 amQuote from: marcov on January 16, 2024, 10:57:39 pmEventually it will be released as 3.4.0, but the exact schedule for that is still unknown. Is there a roadmap of fixes/features we're waiting on for 3.4? Perhaps this will help: https://wiki.freepascal.org/FPC_New_Features_Trunk Revision history shows last change in June 2023. ---------------------------------------------------------------------- deadserious · 2024-01-19 18:44 Quote from: dsiders on January 18, 2024, 12:45:02 amQuote from: deadserious on January 18, 2024, 12:20:23 amQuote from: marcov on January 16, 2024, 10:57:39 pmEventually it will be released as 3.4.0, but the exact schedule for that is still unknown. Is there a roadmap of fixes/features we're waiting on for 3.4? Perhaps this will help: https://wiki.freepascal.org/FPC_New_Features_Trunk Revision history shows last change in June 2023. This looks like a list of new features done waiting for 3.4 release to be included? I'm looking for a list of things that need to be completed before the 3.4 release can be finished. ---------------------------------------------------------------------- PascalDragon · 2024-01-21 11:17 Quote from: deadserious on January 19, 2024, 07:44:01 pmI'm looking for a list of things that need to be completed before the 3.4 release can be finished. There is none. We'll start release preparation for 3.4.0 when we think we've reached a suitable point. This is currently not in sight and even then release preparation takes multiple months. ---------------------------------------------------------------------- Okoba · 2024-02-02 15:51 Hello, This code in mode Delphi crashes, is it a bug? No problem on objfpc mode. Tested on today Trunk. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;   {$mode Delphi}  {$modeswitch functionreferences} type  TTest = reference to procedure(V: Integer);   procedure Test1(V: Integer);  begin    WriteLn('Test1: ', V);  end;   procedure Run;     procedure Test2(V: Integer);    begin      WriteLn('Test2: ', V);    end;   var    T: TTest;  begin    T := @Test2; //Error on Delphi mode but not objfpc    T(1);  end; begin  Run;  ReadLn;end. ---------------------------------------------------------------------- Paolo · 2024-02-02 16:12 What about removing "@" ? ---------------------------------------------------------------------- Okoba · 2024-02-02 17:17 That fixes the problem. But why? As I remember even in Delphi mode, we use @ to address a function. Is there a mode switch to activate using @ in Delphi mode? ---------------------------------------------------------------------- dseligo · 2024-02-03 01:15 Quote from: Okoba on February 02, 2024, 06:17:20 pmThat fixes the problem. But why? As I remember even in Delphi mode, we use @ to address a function. Is there a mode switch to activate using @ in Delphi mode? You use @ to get address, but not to assign procedure or function. See here (first one): https://www.freepascal.org/docs- html/prog/progse74.html ---------------------------------------------------------------------- Okoba · 2024-02-03 06:51 So why compilers does not throw any error? If @ in Delphi mode is not accepted? It throws an error if I want to set a pointer of an integer to another one. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; var  I, J: Integer;begin  I := @J;end.       And on checking again without 'reference to' the variable, it is zero instead of the correct one, raising the question of why this time it accepts the procedure and even runs it, but with the wrong parameters? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;   {$mode Delphi} type  TTest = procedure(V: Integer);   procedure Test1(V: Integer);  begin    WriteLn('Test1: ', V);  end;   procedure Run;     procedure Test2(V: Integer);    begin      WriteLn('Test2: ', V);    end;   var    T: TTest;  begin    T := @Test2;    T(1); //Writes 0  end; begin  Run;  ReadLn;end. ---------------------------------------------------------------------- Paolo · 2024-02-03 14:48 I tested this code Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; //{$mode delphi}{$H+}{$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    procedure Button1Click(Sender: TObject);  private   public   end; var  Form1: TForm1; implementation {$R *.lfm}type   TTest = procedure(V: Integer);   procedure Test2(V: Integer);  begin  form1.caption:='Test2: '+V.tostring;  end;   { TForm1 }   procedure TForm1.Button1Click(Sender: TObject);  var    T: TTest;  begin    T := Test2;    T(3); //Writes 3  end; end. it always works, just in objfpc mode without @ does not compile ---------------------------------------------------------------------- PascalDragon · 2024-02-04 11:28 Quote from: Okoba on February 03, 2024, 07:51:58 amSo why compilers does not throw any error? If @ in Delphi mode is not accepted? Because nobody said that the compiler is flawless. And the function reference functionality is a complicated one... 🙄 Please report a bug. ---------------------------------------------------------------------- Okoba · 2024-02-05 16:00 Reported: 40626 @PascalDragon except this issue, I should say, Function reference works very well and easy, thank you! ---------------------------------------------------------------------- deadserious · 2024-02-06 02:21 Quote from: PascalDragon on January 21, 2024, 12:17:14 pm We'll start release preparation for 3.4.0 when we think we've reached a suitable point. What makes for a "suitable point"? ---------------------------------------------------------------------- Thaddy · 2024-02-06 05:31 'when it is ready', as usual. release management is a lot of work. - documentation ready - test suites run without error - new features completed or decided to postpone - regressions fixed - new platforms done - packaging for all platforms done - release notes written and possibly more ---------------------------------------------------------------------- beria2 · 2025-04-12 20:30 Quote from: Thaddy on February 06, 2024, 06:31:36 am'when it is ready', as usual. release management is a lot of work. - documentation ready - test suites run without error - new features completed or decided to postpone - regressions fixed - new platforms done - packaging for all platforms done - release notes written and possibly more when will support for anonymous functions be added to objfpc? I'm tired of waiting because I don't see anything difficult about it.  :'( :'( :'( :'( :'( :'( :'( ---------------------------------------------------------------------- TRon · 2025-04-12 20:39 Quote from: beria2 on April 12, 2025, 10:30:09 pmwhen will support for anonymous functions be added to objfpc? I'm tired of waiting because I don't see anything difficult about it.  :'( :'( :'( :'( :'( :'( :'( Did you even read the announcement ? Available since may 2022 in trunk so no idea what you're waiting for tbh. ---------------------------------------------------------------------- beria2 · 2025-04-12 22:00 Quote from: TRon on April 12, 2025, 10:39:13 pmQuote from: beria2 on April 12, 2025, 10:30:09 pmwhen will support for anonymous functions be added to objfpc? I'm tired of waiting because I don't see anything difficult about it.  :'( :'( :'( :'( :'( :'( :'( Did you even read the announcement ? Available since may 2022 in trunk so no idea what you're waiting for tbh. Free Pascal Compiler version 3.3.1-17783-gf672d4cccf-dirty [2025/04/10] for x86_64 ----------------------------- {$mode objfpc} begin     procedure(const aArg: String)   begin     Writeln(aArg);   end('Hello World'); number.lpr(897,5) Error: Illegal expression ---------------     {$MODE DELPHI}         begin     procedure(const aArg: String)   begin     Writeln(aArg);   end('Hello World'); Ok! ------------------------------------- I still can't use them.... I'm probably very stupid and I don't understand something in principle. There are a lot of things in fpc that are unclear, which work, which don't, and so they work... ---------------------------------------------------------------------- Martin_fr · 2025-04-12 22:14 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ModeSwitch anonymousfunctions} ---------------------------------------------------------------------- beria2 · 2025-04-16 01:14 Quote from: Martin_fr on April 13, 2025, 12:14:38 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ModeSwitch anonymousfunctions} It's not that simple. Here are my compiler directives:  {$mode objfpc}// Основной режим Object Pascal {$ASMMODE INTEL}           // Синтаксис Intel для ассемблерных блоков {$COPERATORS ON}           // Операторы C-style (+=, -= и т.д.) {$GOTO ON}                 // Разрешает использование меток и goto {$INLINE ON}               // Включение инлайнинга функций {$MACRO ON}                // Поддержка макросов препроцессора // Мод-свитчи: {$MODESWITCH ADVANCEDRECORDS}  // Методы в records {$MODESWITCH ANONYMOUSFUNCTIONS} // Анонимные функции  (sic!) {$MODESWITCH ARRAYOPERATORS}   // Перегрузка операторов для массивов {$MODESWITCH CLASS}           // Полная поддержка классов {$MODESWITCH CLASSICPROCVARS} // Классические процедурные переменные {$MODESWITCH DUPLICATELOCALS} // Разрешение дублирования имен переменных {$MODESWITCH NESTEDPROCVARS}  // Вложенные процедурные переменные {$MODESWITCH OUT}             // Параметры out {$MODESWITCH POINTERTOPROCVAR} // Преобразование указателей в procvar {$MODESWITCH RESULT}          // Использование Result в функциях {$MODESWITCH TYPEHELPERS}     // Хелперы для типов {$PACKENUM 4}            // Минимальный размер перечислений {$PACKRECORDS C}         // C-совместимое выравнивание записей {$PACKSET 4}             // Минимальный размер множеств {$Q-}                    // Проверка переполнения {$R-}                    // Проверка диапазона {$T-}                    // Проверка типизированных указателей                                                      {$H+}                    // Режим расширенного диапазона для типа integer {$S+}                    // Выравнивание полей type   TProc = reference to procedure; Компиляция проекта, режим: Default, цель: z:\Number\number.exe: Код завершения 1, ошибок: 3, подсказок: 3 Hint: Start of reading config file Z:\fpcupdeluxe\fpc\bin\x86_64-win64\fpc.cfg Hint: End of reading config file Z:\fpcupdeluxe\fpc\bin\x86_64-win64\fpc.cfg Verbose: Free Pascal Compiler version 3.3.1-17788-gc6d3e42129-dirty [2025/04/12] for x86_64 Verbose: Copyright (c) 1993-2025 by Florian Klaempfl and others Note: Switching assembler to default source writing assembler Verbose: Target OS: Win64 for x64 Verbose: Compiling number.lpr Verbose: Compiling Bit.pas Bit.pas(187,14) Error: Identifier not found "reference" Bit.pas(187,24) Error: Error in type definition Bit.pas(187,24) Error: Syntax error, ";" expected but "TO" found Verbose: Compilation aborted Verbose: Z:\fpcupdeluxe\fpc\bin\x86_64-win64\ppcx64.exe returned an error exitcode ---------------------------------------------------------------------- TRon · 2025-04-16 01:57 Quote from: Martin_fr on April 13, 2025, 12:14:38 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ModeSwitch anonymousfunctions} Quote from: beria2 on April 16, 2025, 03:14:32 amIt's not that simple. Here are my compiler directives: ... {$MODESWITCH ANONYMOUSFUNCTIONS} // Анонимные функции  (sic!) ... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TProc = reference to procedure;  It is actually that simple because anonymous function <> function reference. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$modeswitch functionreferences}  Two different concepts, two different modeswitches. ---------------------------------------------------------------------- Thaddy · 2025-04-16 04:22 @TRon @beria2 Yes TRon, indeed, let's illustrate the different concepts with two more simple examples: 1. anonymous functions:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$H+}{$modeswitch anonymousfunctions}begin  writeln(function:string             begin                 result := 'hello, World';              end());end.2. function references:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$modeswitch functionreferences}Type  THelloWorld = reference to function:string;    function hello:string;  begin    Result := 'hello';  end;    function world:string;  begin    Result := ', world';  end;  var  hw:THelloWorld;begin  hw :=@hello;  write(hw());  hw :=@world;  writeln(hw());end.1. demonstrates an anonymous function, uses no outer variables and executes the function inline. 2. demonstrates function references, a variable of the type of the function reference can point to and execute different functions with the same prototype. Note that in these examples I use no parameters but in both cases you can use parameters for the functions or procedures. I could not come up with simpler examples. Wait Uhhhh, ----------------------------------------------- warning: just because we can code follows ----------------------------------------------- A contrived example that combines the two  :Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$modeswitch functionreferences}{$modeswitch anonymousfunctions}begin  writeln(function:string         Type          THelloWorld = reference to function:string;            function hello:string;            begin              Result := 'hello';          end;              function world:string;            begin            Result :=', world';            end;                        var hw:THelloWorld;          begin            hw := @hello;            result:=hw();            hw := @world;            result := result+hw();          end());end.I hope the latter will not add to the confusion.. @cdbc Beyond cool innit? ---------------------------------------------------------------------- beria2 · 2025-04-16 11:24 for I := 0 to function: integer   begin     if StrLen < TypeBits then Result := StrLen     else       Result := TypeBits;   end() - 1 do ---------------------------------------------------------------------- Thaddy · 2025-04-16 11:43 That won't work.(I hope... ) Loop variables need to be known at compile time. ---------------------------------------------------------------------- Marc · 2025-04-16 12:25 The loop variable (I) is known. The terminating expression is evaluated before the loop starts so my guess is that this is valid (not that I ever will use it) ---------------------------------------------------------------------- cdbc · 2025-04-16 13:01 Hi Quote@cdbc Beyond cool innit?  Yup  ...and to think, all of this magic happens via interfaces behind the scenes...  Regards Benny ---------------------------------------------------------------------- beria2 · 2025-04-16 13:08 Quote from: Thaddy on April 16, 2025, 01:43:25 pm That won't work.(I hope... ) Loop variables need to be known at compile time. It's working ---------------------------------------------------------------------- Thaddy · 2025-04-16 14:50 My point was 1 and 2, which are educational. The third was a joke.. ---------------------------------------------------------------------- cdbc · 2025-04-16 15:48 Hi Ofc. Thaddy  I wouldn't want to see 3) in any codebase, anytime soon  ...or later for that matter  Regards Benny ---------------------------------------------------------------------- PascalDragon · 2025-04-18 12:53 Quote from: cdbc on April 16, 2025, 03:01:03 pmYup  ...and to think, all of this magic happens via interfaces behind the scenes...  You need some mechanism to track the lifetime of the function reference. And once the capturer object implementing the interface is instantiated it's not that expensive: the increments/decrements of the reference count while passing the interface around and an indirect method call for each invocation of the function reference. ---------------------------------------------------------------------- wfbhappy · 2025-05-21 03:27 fpc4.0  release time ? ---------------------------------------------------------------------- Thaddy · 2025-05-21 05:29 Quote from: wfbhappy on May 21, 2025, 05:27:03 amfpc4.0  release time ? If you have read the whole thread: Unknown. When it is ready and when enough resources are available for release manament. This is complex and time consuming for such a small core team. The next release will be a point release and will not contain many of the new features. 3.2.4. 4.0 contains many new features, but there are also quite some outstanding bugs. If you want to use the new features, simply use trunk/main. That is also highly educational and you may even help to find and solve any bug or write examples. Patience,. ---------------------------------------------------------------------- munair · 2025-05-21 23:15 Quote from: Thaddy on April 16, 2025, 01:43:25 pm That won't work.(I hope... ) Loop variables need to be known at compile time. Not necessarily. A for loop needs two numeric expressions. If an expression like a := b + myfunc(); is allowed, then why not for i := b + func1() to c + func2() do ...? The compiler generates the code for each expression before commencing the loop, so there is no reason why that shouldn't work, unless the compiler explicitly forbids it. ---------------------------------------------------------------------- munair · 2025-05-21 23:26 Quote from: beria2 on April 16, 2025, 01:24:08 pm  for I := 0 to function: integer   begin     if StrLen < TypeBits then Result := StrLen     else       Result := TypeBits;   end() - 1 do It made me actually quite curious, but the SharpBASIC compiler supports this construct, because the parser expects expressions, which functions are a part of. I never tried it before. It outputs 0..9: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ' SharpBASIC for-loop with functions' ----------------------------------incl "lib/sys.sbi"; decl func f1():int;decl func f2():int; dim i:itr; main do  for i = f1() to f2() :++ do    print(i);  end  print();end func f1():int do f1 = 0; endfunc f2():int do f2 = 9; end  But it needs a valid function reference. The keyword func or function alone is of course not enough. ---------------------------------------------------------------------- PascalDragon · 2025-05-22 20:04 Quote from: Thaddy on April 16, 2025, 01:43:25 pm That won't work.(I hope... ) Loop variables need to be known at compile time. That does work, after all its only calling a function (no matter how bad it looks). The ranges of the loop don't need to be known at compile time (cause otherwise any for i := 0 to SomeList.Count do-loop would not work). The important point is that the ranges are only evaluated once and that is guaranteed by the code that the compiler generates. Quote from: wfbhappy on May 21, 2025, 05:27:03 amfpc4.0  release time ? Considering that it's not even clear whether the next major release will be 3.4 or 4.0 I'd say 4.0 will be released at least in this century. 🤷‍♀️ ---------------------------------------------------------------------- Bart · 2025-05-22 20:14 Quote from: PascalDragon on May 22, 2025, 10:04:04 pm... I'd say 4.0 will be released at least in this century. 🤷‍♀️ Are you sure? Bart ---------------------------------------------------------------------- PascalDragon · 2025-05-25 19:36 Quote from: Bart on May 22, 2025, 10:14:44 pmQuote from: PascalDragon on May 22, 2025, 10:04:04 pm... I'd say 4.0 will be released at least in this century. 🤷‍♀️ Are you sure? I'm an optimist, so I'm at least confident 😅 ---------------------------------------------------------------------- d2010 · 2025-08-24 21:28 Quote from: PascalDragon on May 26, 2022, 09:47:14 pmDear 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. Why You using  same syntax, perhaps overflow-up too much the tokenizer? I think you replace "procedure" with "procedure.ref"       function with funct.ref You cannot use You must quote name of procedure  with other chars, diferent  '' ' ' ' ' ''   F"nop"   double 0quotes "" The type string of too good, and you   Code: [Select]  p := procedure(begin.ref,Writeln('Foobar'),end)   p(); Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFunc = function: LongInt; var  p: TProcedure;  f: TFunc;  n: TNotifyEvent;begin  procedure("Nop",const aArg: String)  begin    Writeln(aArg);  end('Hello World');    p := procedure("Nop",Writeln('Foobar');end;  p();  n := procedure("Nop",aSender: TObject);       begin             Writeln(HexStr(Pointer(aSender));           end;  n(Nil);    f := function("MyRes" : LongInt;       begin             "MyRes" := 42;         end;  Writeln(f());end.          Old codes , previous here -- Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFunc = function: LongInt; var  p: TProcedure;  f: TFunc;  n: TNotifyEvent;begin  procedure(const aArg: String)  begin    Writeln(aArg);  end('Hello World');    p := procedure       begin           Writeln('Foobar');           end;  p();    n := procedure(aSender: TObject);       begin             Writeln(HexStr(Pointer(aSender));           end;  n(Nil);    f := function(MyRes : LongInt;       begin             MyRes := 42;         end;  Writeln(f());end. ---------------------------------------------------------------------- Thaddy · 2025-08-25 06:10 The syntax is delphi compatible. ---------------------------------------------------------------------- PascalDragon · 2025-09-02 19:12 Quote from: d2010 on August 24, 2025, 11:28:02 pmWhy You using  same syntax, perhaps overflow-up too much the tokenizer? This feature is intended to be Delphi-compatible, thus the Delphi- compatible syntax is used. It makes no sense to invent a different syntax when we need the Delphi-compatible one anyway. Nothing more, nothing less. ---------------------------------------------------------------------- cocce · 2026-01-18 11:06 Do you know when it will be released? Thanks in advance. ---------------------------------------------------------------------- PascalDragon · 2026-01-18 14:59 Quote from: cocce on January 18, 2026, 12:06:51 pmDo you know when it will be released? No. ---------------------------------------------------------------------- Thaddy · 2026-01-19 05:55 If you do not use FreePascal in any professional capacity, but only as a semi-professional or a hobbyist, why don't you simply use trunk? I think that most users that call for a release that contains  certain new features really do not need a "release" for that. ---------------------------------------------------------------------- PascalDragon · 2026-01-20 20:50 Quote from: wfbhappy on January 19, 2026, 02:44:42 am有点慢 Please only use English in the international part of the forum or provide a translation with your post. ---------------------------------------------------------------------- vfclists · 2026-02-05 10:10 Quote from: Thaddy on January 19, 2026, 06:55:15 amIf you do not use FreePascal in any professional capacity, but only as a semi- professional or a hobbyist, why don't you simply use trunk? I think that most users that call for a release that contains  certain new features really do not need a "release" for that. Is trunk good enough and safe enough for the current Lazarus 4.xx? Are enough users happy with it? The comments in this thread don't reassure me Status of FPC 3.4.0 or FPC 4.0.0 [major release] ---------------------------------------------------------------------- Thaddy · 2026-02-05 10:13 About 30-40% use trunk. For all intend and purpose it feels stable but there are many open bugs of differing urgency. Most people won't even notice any bugs. ---------------------------------------------------------------------- 440bx · 2026-02-05 10:19 Quote from: Thaddy on February 05, 2026, 11:13:00 amMost people won't even notice any bugs. That alone is an indication of a significant problem. I cannot even port the Windows API definitions without running into FPC bugs and deficiencies.    IOW, it doesn't even take writing code to run into problems, just declarations/definitions does it. How about that for a "new feature" ?... being able to faithfully and correctly port the Windows API from C to FPC.  Personally, I think that would be a great "new feature" (and a rather useful one too.) ---------------------------------------------------------------------- ALLIGATOR · 2026-02-05 10:23 I just want to note that FPC 3.2.2 and 3.2.4 also have bugs, just like FPC[main] And it's still a big question where there are more of them ---------------------------------------------------------------------- PascalDragon · 2026-02-05 20:29 Quote from: 440bx on February 05, 2026, 11:19:45 amQuote from: Thaddy on February 05, 2026, 11:13:00 amMost people won't even notice any bugs. That alone is an indication of a significant problem. I cannot even port the Windows API definitions without running into FPC bugs and deficiencies.    IOW, it doesn't even take writing code to run into problems, just declarations/definitions does it. How about that for a "new feature" ?... being able to faithfully and correctly port the Windows API from C to FPC.  Personally, I think that would be a great "new feature" (and a rather useful one too.) But that is not a new problem in 3.3.1. ---------------------------------------------------------------------- wpostma · 2026-04-21 01:25 I have built the head of the main branch on FPC, are there unit tests in the FPC repo, for this new reference to keyword and delphi-compat language feature? ---------------------------------------------------------------------- Thaddy · 2026-04-21 10:41 a) in Delphi mode they are already automatically activated in current trunk, in Objfpc mode you have to add the modeswitches manually. b) yes, many, see the release notes for the features in https://wiki.f reepascal.org/FPC_New_Features_Trunk#Support_for_Function_References_a nd_Anonymous_Functions and follow the commit. I don't remember the particular tests from memory. Both features are Delphi compatible. (near, but not 100%, explained in the feature announcement) There are also some examples in the wiki, like the third example in the smartpointers subject (which I wrote) but there are more. Also many examples on the forum. Simple example:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode delphi}uses sysutils,classes;var  List:TStringlist;  i:integer;begin  List := TStringlist.Create;  AddExitProc(procedure begin list.free;end);// anonymous  for i := 0 to 9 do List.Add(i.ToString);  writeln(list.text);  readln;end.  In general, in mode Delphi, almost everything, if not all, works for both features, but read the announcement. All examples from Barry Kelly - former Delphi compiler engineer - work for example. As do all of the examples regarding the two features from the public repository from Embarcadero. ---------------------------------------------------------------------- marcov · 2026-04-21 11:24 And at least the basic support has been added to TThread, like queue() has the anonymous method overhead. ---------------------------------------------------------------------- Thaddy · 2026-04-21 11:47 @Marcov Well, there is some overhead, but writing things like:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode delphi}uses sysutils, classes;var  T:TThread;begin  T := TThread.ExecuteInThread(procedure //some long running code                             begin                                 writeln('Thread start...');                                 // load simulation                                 Sleep(5000);                               writeln('Thread finished');                             end);  T.Waitfor;// don't leave the waits out...  writeln('Done');  readln;end. is great. The implicit COM interface setup code has no noticeable effect on the actual running code except in high performance threaded applications. More elaborate, with good practice, here: https://forum.lazarus.freepascal.org/index.php/topic,72079.msg563706.h tml#msg563706 Demonstrates that even with anon procedures the code is considerably faster  than the sequential approach. (The subject of that forum thread does not matter) ---------------------------------------------------------------------- PascalDragon · 2026-04-23 20:04 Quote from: wpostma on April 21, 2026, 03:25:35 amI have built the head of the main branch on FPC, are there unit tests in the FPC repo, for this new reference to keyword and delphi-compat language feature? The tests are tests/test/tfuncref*.pp and tests/test/tanonfunc*.pp. ---------------------------------------------------------------------- lebao3105 · 2026-05-06 13:04 I have the following piece of code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program test;{$modeswitch functionreferences}{$modeswitch anonymousfunctions} type    generic TTestFunction = reference to function(arg: string): boolean; generic Procedure Testit(callback: specialize TTestFunction);begin    callback('Hello world from TestIt!');end; Begin    specialize TestIt(        function (arg: string): boolean        begin            writeln(arg);          exit (true);        end    );End.  FPC's complaination: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function (arg: string): boolean                      ^ indentier expected but ":" found  However the code compiles with ObjFPC / Delphi mode used. Like...why? Update: Found the reason. I checked my usages of the same function type, and yes, {$modeswitch result} once again helped me. ---------------------------------------------------------------------- Thaddy · 2026-05-07 14:19 The other modes are not using Classes. You need to choose a mode that supports classes. ---------------------------------------------------------------------- PascalDragon · 2026-05-12 18:57 Quote from: lebao3105 on May 06, 2026, 03:04:09 pmUpdate: Found the reason. I checked my usages of the same function type, and yes, {$modeswitch result} once again helped me. For modes without $ModeSwitch Result enabled you need to add a name for your result variable just like for operator overloads in those modes. So your code would become this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    specialize TestIt(        function (arg: string) res : boolean        begin            writeln(arg);            exit (true);        end    );