Recent

Author Topic: nested functions  (Read 5642 times)

JdeHaan

  • Full Member
  • ***
  • Posts: 171
nested functions
« on: April 30, 2020, 11:56:21 am »
Hi,
I was experimenting with nested functions and translated below code from a functional language. It does compile but doesn't provide the correct output. I get sort of garbage numbers from the first two writeln statements. I assume the nested function doesn't have access to the x, y params. How can this be solved? Any ideas?

Code: Pascal  [Select][+][-]
  1. program testnested;
  2. {$Mode objfpc} {$H+}
  3. {$ModeSwitch nestedprocvars}
  4.  
  5. uses sysutils;
  6.  
  7. type
  8.   TClosure = function(Point: char): integer is nested;
  9.  
  10. function MakePoint(x, y: integer): TClosure;
  11.  
  12.   function Closure(Point: char): integer;
  13.   begin
  14.     case Point of
  15.       'x': Result := x;
  16.       'y': Result := y;
  17.       else
  18.         writeln('unknown point: ', Point);
  19.         Result := -1;
  20.     end;
  21.   end;
  22.  
  23. begin
  24.   Result := @Closure;
  25. end;
  26.  
  27. var
  28.   Point: TClosure;
  29.  
  30. begin
  31.   Point := MakePoint(2,3);
  32.   writeln(Point('x')); // should print 2
  33.   writeln(Point('y')); // should print 3
  34.   writeln(Point('z'));
  35. end.
  36.  


MacOs Catalina, Laz 2.1.0, FPC 3.3.1

PascalDragon

  • Hero Member
  • *****
  • Posts: 6354
  • Compiler Developer
Re: nested functions
« Reply #1 on: April 30, 2020, 12:04:50 pm »
Nested functions are not closures. You can use is nested functions only while the stack frame of the containing function is alive. In your example it means that you can only call it inside MakePoint (or pass it to some other code inside that function).

JdeHaan

  • Full Member
  • ***
  • Posts: 171
Re: nested functions
« Reply #2 on: April 30, 2020, 12:31:42 pm »
Thanks PascalDragon

I was trying to store data in a function. Not possible I guess without real closures.

devEric69

  • Hero Member
  • *****
  • Posts: 652
Re: nested functions
« Reply #3 on: April 30, 2020, 01:27:47 pm »
A static variable (as in VB6), or a imported variable of a superior closure (as the "use" keyword in front of anonymous function in Php) do not exist exactly and identically in the Object Pascal grammar.

Such a variable must be stored in a normal global variable, or in a so-called "Class variable". Say in another way, when a function is unstacked, all the variables used by this function are lost, if they are not stored elsewhere.

See https://forum.lazarus.freepascal.org/index.php?topic=5572.0 .
« Last Edit: April 30, 2020, 01:32:51 pm by devEric69 »
use: Linux 64 bits (Ubuntu 20.04 LTS).
Lazarus version: 2.0.4 (svn revision: 62502M) compiled with fpc 3.0.4 - fpDebug \ Dwarf3.

JdeHaan

  • Full Member
  • ***
  • Posts: 171
Re: nested functions
« Reply #4 on: April 30, 2020, 01:55:28 pm »
Thanks for the hint. Still working on my above example, however this one now works as expected.

Code: Pascal  [Select][+][-]
  1. program counter;
  2. {$modeswitch nestedprocvars}
  3.  
  4. type
  5.   TProc = procedure is nested;
  6.  
  7. var
  8.   i: Integer=0; // moved from inside MakeCounter to global var.
  9.  
  10. function MakeCounter: TProc;
  11.  
  12.   procedure Count;
  13.   begin
  14.     Inc(i);
  15.     Writeln(i);
  16.   end;
  17.  
  18. begin
  19.   Result := @Count;
  20. end;
  21.  
  22. var Count: TProc;
  23.  
  24. begin
  25.   Count := MakeCounter;
  26.   Count;  // 1
  27.   Count;  // 2
  28.   Count;  // 3
  29.   Count;  // 4
  30. end.
  31.  

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: nested functions
« Reply #5 on: April 30, 2020, 02:05:42 pm »
I was trying to store data in a function. Not possible I guess without real closures.
You can store data in a function in {$J+} state using typed constants. Maybe this is helpful.
Example:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}{$J+}
  2. function PersistData(const newdata: integer = 0;reset:boolean = false):integer;
  3. const
  4.   Store:integer = 0;
  5. begin
  6.   if newdata <> 0 then store := NewData;
  7.   if reset then Result :=0 else Result := Store;  
  8. end;
  9.  
  10. var
  11.   a:integer;
  12. begin
  13.   a := PersistData; // 0;
  14.   writeln(a);
  15.   a:= PersistData(100); // store and return 100;
  16.   writeln(PersistData);  //100
  17.   writeln(PersistData);  // still 100
  18.   writeln(Persistdata(0,true)); // reset store to zero
  19. end.

AKA
I did not test this yet for nested functions. I did, it works.
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2. function metapersist(const newdata:integer = 0;reset:Boolean = false):integer;
  3.   // local
  4.   {$push}{$J+}
  5.   function PersistData(const newdata: integer = 0;reset:boolean = false):integer;
  6.   const
  7.     Store:integer = 0;
  8.   begin
  9.     if newdata <> 0 then store := NewData;
  10.     if reset then Result :=0 else Result := Store;  
  11.   end;
  12.   {$pop}
  13. begin
  14.   Result := PersistData(Newdata, reset);
  15. end;
  16.  
  17. var
  18.   a:integer;
  19. begin
  20.   a := metapersist; // 0;
  21.   writeln(a);
  22.   a:= MetaPersist(100); // store and return 100;
  23.   writeln(MetaPersist);  //100
  24.   writeln(MetaPersist);  // still 100
  25.   writeln(MetaPersist(0,true)); // reset store to zero
  26. end.
« Last Edit: April 30, 2020, 02:19:35 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

JdeHaan

  • Full Member
  • ***
  • Posts: 171
Re: nested functions
« Reply #6 on: April 30, 2020, 02:25:39 pm »
Thanks Taddy

Got this now right:

Code: Pascal  [Select][+][-]
  1. program counter;
  2. {$mode objfpc}{$H+}{$J+}
  3. {$modeswitch nestedprocvars}
  4.  
  5. type
  6.   TProc = procedure is nested;
  7.  
  8. function MakeCounter: TProc;
  9. const
  10.   i: Integer=0;
  11.  
  12.   procedure Count;
  13.   begin
  14.     Inc(i);
  15.     Writeln(i);
  16.   end;
  17.  
  18. begin
  19.   Result := @Count;
  20. end;
  21.  
  22. var Count: TProc;
  23.  
  24. begin
  25.   Count := MakeCounter;
  26.   Count;  // 1
  27.   Count;  // 2
  28.   Count;  // 3
  29.   Count;  // 4
  30. end.
  31.  

The one in the first post... can't get it to work.

JdeHaan

  • Full Member
  • ***
  • Posts: 171
Re: nested functions
« Reply #7 on: April 30, 2020, 03:19:08 pm »
Got it working !

Code: Pascal  [Select][+][-]
  1. program testnested;
  2. {$mode objfpc}{$H+}{$J+}
  3. {$ModeSwitch nestedprocvars}
  4.  
  5. uses sysutils;
  6.  
  7. type
  8.   TNestedFunc = function(Point: char): integer is nested;
  9.  
  10.  
  11. function MakePoint(x, y: integer): TNestedFunc;
  12. const
  13.   x_: Integer = 0;
  14.   y_: Integer = 0;
  15.  
  16.   {$push}
  17.   function NestedFunc(Point: char): integer;
  18.   begin
  19.     case Point of
  20.       'x': Result := x_;
  21.       'y': Result := y_;
  22.       else
  23.         writeln('unknown point: ', Point);
  24.         Result := -1;
  25.     end;
  26.   end;
  27.   {$pop}
  28.  
  29. begin
  30.   x_ := x;
  31.   y_ := y;
  32.   Result := @NestedFunc;
  33. end;
  34.  
  35. var
  36.   Point: TNestedFunc;
  37.  
  38. begin
  39.   Point := MakePoint(2,3);
  40.   writeln(Point('x')); // should print 2
  41.   writeln(Point('y')); // should print 3
  42.   writeln(Point('z'));
  43. end.
  44.  

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: nested functions
« Reply #8 on: April 30, 2020, 03:28:27 pm »
If you use push pop, the you can skip the {$J+} at the top and add {$J+} after {$push}.
How you wrote it it does nothing, although it works, because {$J+} is now global in your code.

Explanation:
{$push} saves the default state of the switches, now you can modify local switchtes by adding {$J+} and {$pop} will restore the default switches afterwards.
Normally the default state is {$J-}
So  if you are happy with global {$J+} you can remove the {push/pop. otherwise remove it at the to and add {$J+} after {$push} (recommended)
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

 

TinyPortal © 2005-2018