Recent

Author Topic: Feature announcement: Function References and Anonymous Functions  (Read 58352 times)

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #45 on: December 07, 2022, 12:32:30 pm »
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.

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

  • Hero Member
  • *****
  • Posts: 3156
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Feature announcement: Function References and Anonymous Functions
« Reply #46 on: December 07, 2022, 01:12:54 pm »
Peter, please look at this thread - https://forum.lazarus.freepascal.org/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
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

lazycat2

  • New member
  • *
  • Posts: 8
Re: Feature announcement: Function References and Anonymous Functions
« Reply #47 on: December 08, 2022, 07:51:47 pm »
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
Thanks. I had a suspicion that I have to build it - now it's confirmed :)

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #48 on: December 11, 2022, 05:39:01 pm »
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
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

  • Full Member
  • ***
  • Posts: 233
    • Highball Github
Re: Feature announcement: Function References and Anonymous Functions
« Reply #49 on: December 17, 2022, 05:59:01 am »
No idea how I missed this announcement back in May but this is great! Looking forward to slinging some anon functions around!

flowCRANE

  • Hero Member
  • *****
  • Posts: 885
Re: Feature announcement: Function References and Anonymous Functions
« Reply #50 on: June 05, 2023, 07:46:48 pm »
In the first post of this thread, the following demo is provided:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFunc = function: LongInt;
  3.  
  4. var
  5.   p: TProcedure;
  6.   f: TFunc;
  7.   n: TNotifyEvent;
  8. begin
  9.   procedure(const aArg: String)
  10.   begin
  11.     Writeln(aArg);
  12.   end('Hello World');
  13.  
  14.   p := procedure
  15.        begin
  16.              Writeln('Foobar');
  17.            end;
  18.   p();
  19.  
  20.   n := procedure(aSender: TObject);
  21.        begin
  22.              Writeln(HexStr(Pointer(aSender));
  23.            end;
  24.   n(Nil);
  25.  
  26.   f := function MyRes : LongInt;
  27.        begin
  28.              MyRes := 42;
  29.            end;
  30.   Writeln(f());
  31. end.

Can somebody explain me what the hell is this highlighted monster? :o

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! 8)
« Last Edit: June 05, 2023, 08:48:25 pm by furious programming »
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5752
  • Compiler Developer
Re: Feature announcement: Function References and Anonymous Functions
« Reply #51 on: June 05, 2023, 09:25:00 pm »
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?

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

  • Jr. Member
  • **
  • Posts: 85
Re: Feature announcement: Function References and Anonymous Functions
« Reply #52 on: June 05, 2023, 09:54:40 pm »
There isn't much use for this explicit functionality

This is a workaround for not having inline variables! :D

PascalDragon

  • Hero Member
  • *****
  • Posts: 5752
  • Compiler Developer
Re: Feature announcement: Function References and Anonymous Functions
« Reply #53 on: June 05, 2023, 10:00:15 pm »
There isn't much use for this explicit functionality

This is a workaround for not having inline variables! :D

Much more verbose than just scrolling up and declaring the variable there...  🙄

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #54 on: June 06, 2023, 01:07:55 am »
Can possibly been used to capture variables by value:

Code: Pascal  [Select][+][-]
  1.  
  2. var
  3.   i,j,k:integer;
  4.  
  5. .....
  6. ......
  7. begin
  8. ....
  9. ....
  10.  
  11. procedure(i,j,k:integer)
  12. begin
  13.     Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  14. end(i,j,k);  
  15. inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value
  16.  

I did not test if this really works....
« Last Edit: June 06, 2023, 10:22:19 am by Peter H »

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #55 on: June 06, 2023, 09:09:31 am »
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
« Last Edit: June 06, 2023, 11:07:44 am by Peter H »

Bad Sector

  • Jr. Member
  • **
  • Posts: 69
    • Runtime Terror
Re: Feature announcement: Function References and Anonymous Functions
« Reply #56 on: June 06, 2023, 04:11:36 pm »
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][+][-]
  1.       // LLightRows.Sort(@LightRowSorter, Self);
  2.       LLightRows.Sort(function(const A, B: TLightRow): SizeInt // <- i get the internal error here
  3.         var
  4.           AP, BP: TVector;
  5.           SA, SB: Single;
  6.         begin
  7.           AP:=ViewMatrix.Transformed(PVector(@A)^);
  8.           ProjectionMatrix.TransformProj(AP);
  9.           BP:=ViewMatrix.Transformed(PVector(@B)^);
  10.           ProjectionMatrix.TransformProj(BP);
  11.           SA:=AP.Z*10000 + AP.X;
  12.           SB:=BP.Z*10000 + BP.X;
  13.           if SA < SB then Exit(-1);
  14.           if SA > SB then Exit(1);
  15.           Result:=0;
  16.         end
  17.       );
  18.  

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<TLightRow>" and TDynArray is a generic container:

Code: Pascal  [Select][+][-]
  1.   generic TDynArray<T> = object(specialize TDynArrayView<T>)
  2.   type
  3.     // Callback function for sorting
  4.     TSorterWithData = function(const A, B: T; Data: Pointer): SizeInt;
  5.     TSorter = function(const A, B: T): SizeInt;
  6.  
  7.   ...
  8.  
  9.     // Sort the array using the given sorting function with the passed data
  10.     procedure Sort(ASorter: TSorterWithData; AData: Pointer);
  11.     // Sort the array using the given sorting function
  12.     procedure Sort(ASorter: TSorter);
  13.  

(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.
Kostas "Bad Sector" Michalopoulos
Runtime Terror

PascalDragon

  • Hero Member
  • *****
  • Posts: 5752
  • Compiler Developer
Re: Feature announcement: Function References and Anonymous Functions
« Reply #57 on: June 06, 2023, 09:55:50 pm »
Can possibly been used to capture variables by value:

Code: Pascal  [Select][+][-]
  1.  
  2. var
  3.   i,j,k:integer;
  4.  
  5. .....
  6. ......
  7. begin
  8. ....
  9. ....
  10.  
  11. procedure(i,j,k:integer)
  12. begin
  13.     Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  14. end(i,j,k);  
  15. inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value
  16.  

I did not test if this really works....

This is the same as

Code: Pascal  [Select][+][-]
  1.  
  2. procedure NestedFunc(i,j,k:integer)
  3. begin
  4.     Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  5. end;  
  6.  
  7. var
  8.   i,j,k:integer;
  9.  
  10. .....
  11. ......
  12. begin
  13. ....
  14. ....
  15.  
  16. NestedFunc(i,j,k);
  17. inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value
  18.  

Which is the same as the following:

Code: Pascal  [Select][+][-]
  1. var
  2.   i,j,k:integer;
  3.  
  4. .....
  5. ......
  6. begin
  7. ....
  8. ....
  9.  
  10. Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  11. inc(i); // i is iterated BEFORE it is printed, but its old value will be printed, because it was captured by value
  12.  

So the outer anonymous function is essentially useless.

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:

[ … ]

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

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #58 on: June 06, 2023, 11:17:10 pm »
So the outer anonymous function is essentially useless.

I do not think so. This would be useless:
Code: Pascal  [Select][+][-]
  1. procedure(var i,j,k:integer)
  2. begin
  3.     Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  4. end(i,j,k);
  5.  
  6. inc(i);
  7. .......
  8.  
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][+][-]
  1. procedure(i,j,k:integer)
  2. begin
  3.     Queue(procedure begin Sleep(500); Writeln(i,' ',hexstr(@i),' ',j,' ',k);end);
  4. end(i,j,k);
  5.  
  6. inc(i);
  7. .......
  8.  
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][+][-]
  1. procedure(i2,j2,k2:integer)
  2. begin
  3.     Queue(procedure begin Sleep(500); Writeln(i2,' ',hexstr(@i2),' ',j2,' ',k2);end);
  4. 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][+][-]
  1. const pp= procedure(i2,j2,k2:integer)
  2. begin
  3.     Queue(procedure begin Sleep(500); Writeln(i2,' ',hexstr(@i2),' ',j2,' ',k2);end);
  4. end;
  5. pp(i,j,k);
  6.  

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.


« Last Edit: June 07, 2023, 09:07:02 am by Peter H »

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: Feature announcement: Function References and Anonymous Functions
« Reply #59 on: June 07, 2023, 03:31:58 pm »
So 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][+][-]
  1. type  Tpr = reference to procedure;
  2. var   p_inner: Tpr;
  3.       str: String;
  4. begin
  5.  
  6.     str := 'Line2: Hello World!';
  7.  
  8.     const p_outer = procedure(s:String)
  9.     begin
  10.       p_inner := procedure
  11.       begin
  12.          Writeln(s);
  13.       end;
  14.     end;
  15.     p_outer(str);  //This line prints nothing,
  16.            //it just instantiates p_outer and p_inner and captures their local context.
  17.     str := '--------------This is NOT printed!----------------';
  18.  
  19.     Writeln('Line1: p_outer was called, now calling p_inner');
  20.     p_inner();  //This line prints the string s="Line2...", which was captured by p_outer.
  21.  
  22.     readln;
  23. end.
  24.  
  25.  

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][+][-]
  1.  
  2. type  Tpr = reference to procedure;
  3. var   p_inner: Tpr;
  4.       str: integer;
  5. begin
  6.  
  7.     str := 1;
  8.  
  9.     procedure(s:integer)
  10.     begin
  11.       p_inner := procedure
  12.       begin
  13.          Writeln(s);
  14.       end;
  15.     end(str);
  16.     //p_outer(str);  //This line prints nothing,
  17.            //it just instantiates p_outer and p_inner and captures their local context.
  18.     str := 2;//'--------------This is NOT printed!----------------';
  19.  
  20.     Writeln('Line1: p_outer was called, now calling p_inner');
  21.     p_inner();  //This line prints the string s="Line2...", which was captured by p_outer.
  22.  
  23.     readln;
  24. end.
  25.  
« Last Edit: June 07, 2023, 08:03:01 pm by Peter H »

 

TinyPortal © 2005-2018