Lazarus

Programming => General => Topic started by: rnfpc on June 22, 2019, 10:38:53 am

Title: Functional programming in Pascal
Post by: rnfpc on June 22, 2019, 10:38:53 am
Are there functional programming methods in Pascal such as map, filter, apply, reduce, fold, foldl and foldr function? I could not find them on searching the internet. Possibly Pascal is a purely imperative language.

If not, is it possible to create a map function so that one can sent a list/array to it with a function and it return a new list/array with sent function applied to each element of sent list:

Code: Pascal  [Select][+][-]
  1. outlist := map(sentfunction, sentlist);

where sentfunction is a function which takes only one item and returns a modified item.
Title: Re: Functional programming in Pascal
Post by: marcov on June 22, 2019, 10:47:18 am
Pascal is mixed imperative-OOP. Conceptually the same level as C++.

There are no such functions. If they rely on functional lazy evaluation there also would be no point.
Title: Re: Functional programming in Pascal
Post by: rsz on June 22, 2019, 11:58:06 am
I was also wondering about this. Does FreePascal support anonymous functions or alike? something like this?

Code: Pascal  [Select][+][-]
  1. x := MyFunction(procedure begin
  2.     // do stuff
  3.   end)

One could easily implement map, fold, etc. if functions are to become first class in FreePascal. This would also open the door for other interesting concepts, such as easy "async" execution of functions. e.g.:
Code: Pascal  [Select][+][-]
  1. context := ExecuteAsync(procedure begin
  2.     // do stuff in another thread
  3.   end);
  4. // do stuff
  5. context.Await;

I'm still pretty new to FreePascal, so maybe something as easy as this is already possible?
Title: Re: Functional programming in Pascal
Post by: marcov on June 22, 2019, 12:12:28 pm
I was also wondering about this. Does FreePascal support anonymous functions or alike? something like this?

Code: Pascal  [Select][+][-]
  1. x := MyFunction(procedure begin
  2.     // do stuff
  3.   end)

Being worked on by an external contributor.  Slow progress.

Quote
One could easily implement map, fold, etc. if functions are to become first class in FreePascal. This would also open the door for other interesting concepts, such as easy "async" execution of functions. e.g.:
Code: Pascal  [Select][+][-]
  1. context := ExecuteAsync(procedure begin
  2.     // do stuff in another thread
  3.   end);
  4. // do stuff
  5. context.Await;

I'm still pretty new to FreePascal, so maybe something as easy as this is already possible?

Not yet. Delphi has some such features, though there are caveats (capturing the state of the anonymous function doesn't always goes right afaik). I use it in Delphi, but try to avoid complex parametrisation.

But it remains a bit of a kludge since it is bolted on functionality, rather than being in the core of the language like with functional languages. Also compiletime typing might restrict you from emulating functional language interpreters.

Lot of work, low gains. Rather than thinking in copying over language functionality, better restate your problem as cleanly as possible, and find a better way.
Title: Re: Functional programming in Pascal
Post by: julkas on June 22, 2019, 03:17:46 pm
Are there functional programming methods in Pascal such as map, filter, apply, fold, foldl and foldr function? I could not find them on searching the internet. Possibly Pascal is a purely imperative language.
I know great map, filter, reduce,.. from Python!
My opinion - you should first think about how to describe, implement your data structure, algorithm in Pascal language and not about language features. It's not easy. Each programming language has it's own spirit, philosophy, strength and weakness.
Regards.

P.S. You can write your own map, filter, reduce,..
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 22, 2019, 05:42:21 pm
I was also wondering about this. Does FreePascal support anonymous functions or alike? something like this?

Code: Pascal  [Select][+][-]
  1. x := MyFunction(procedure begin
  2.     // do stuff
  3.   end)

One could easily implement map, fold, etc. if functions are to become first class in FreePascal.
As marcov wrote this is not yet possible, but you can do something like this (needs FPC 3.2.0 or newer):
Code: Pascal  [Select][+][-]
  1. program tmaptest;
  2.  
  3. {$mode objfpc}
  4.  
  5. type
  6.   generic TMapFunc<T> = function(constref aElem: T): T;
  7.  
  8. generic function Map<T>(aMapFunc: specialize TMapFunc<T>; constref aArr: specialize TArray<T>): specialize TArray<T>;
  9. var
  10.   i: SizeInt;
  11. begin
  12.   Result := Nil;
  13.   SetLength(Result, Length(aArr));
  14.   for i := 0 to High(aArr) do
  15.     Result[i] := aMapFunc(aArr[i]);
  16. end;
  17.  
  18. function TimesTwo(constref aArg: LongInt): LongInt;
  19. begin
  20.   Result := aArg * 2;
  21. end;
  22.  
  23. var
  24.   arr: array of LongInt;
  25.   i: LongInt;
  26. begin
  27.   arr := specialize Map<LongInt>(@TimesTwo, [3, 5, 6, 8]);
  28.   for i in arr do
  29.     Writeln(i);
  30. end.

Something like this can also be done for the other functions. Maybe one could implement a unit for functional algorithms like this and switch them over to anonymous routines once they're integrated into trunk. *shrugs*
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 22, 2019, 07:38:48 pm
What is the problem that anonymous functions solve? I don't get it.
Title: Re: Functional programming in Pascal
Post by: lainz on June 22, 2019, 07:57:07 pm
What is the problem that anonymous functions solve? I don't get it.

I think it depends on context. In JavaScript for example you can combine an anonymous function with an arrow function to access scope variables.

Code: Javascript  [Select][+][-]
  1. var a = 10; // this is inside some other function, not a global variable
  2. myarr.filter(value=>{
  3.   return value == a; // can access "a" var
  4. })

the "value => {}" is an anonymous arrow function with a single parameter. You can do something like:

Code: Javascript  [Select][+][-]
  1. var a = 10; // this is inside some other function, not a global variable
  2. myarr.filter(function (value) {
  3.   return value == 10; // cant access "a" here
  4. })

But you can't access "a" inside that function.

With FPC the paradigm is OOP, so if you work with objects and classes, there is no need of sharing the scope, since is self contained in the class. In other words in a class you can always access "a" because it for example is declared in the private section, and the filter function is inside the class.

If you don't reuse the function, and is used in a single place, like filtering with a certain criteria a single time, why declaring a function in the namespace for it? That means that function is used only there and should not be used anywhere. That's is the thing I understand to use them.

If you think with functions it has sense. If you think with classes, maybe not, because you can always declare a private function that is not used outside. That doesn't happen in JavaScript (private doesn't exist as easy), for example where is allowed anonymous functions.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 22, 2019, 11:00:25 pm
Something like this?

Code: Pascal  [Select][+][-]
  1. function IsNegative(SomeParam: Integer): Boolean;
  2. var
  3.   SomeVar: Integer;
  4.  
  5.   function Calculate: Boolean;
  6.   begin
  7.     Result := False;
  8.     if SomeVar < 0 then Result := True;
  9.   end;
  10.  
  11. begin
  12.   Result := Calculate;
  13. end;

They copies it from Pascal  ;)
Title: Re: Functional programming in Pascal
Post by: lainz on June 23, 2019, 12:50:48 am
Exactly that, you get the same benefits and problems of doing that, specially knowing what exactly is in scope and what isn't.
Title: Re: Functional programming in Pascal
Post by: jamie on June 23, 2019, 01:31:55 am
Code: Pascal  [Select][+][-]
  1. type
  2.  
  3.   { TForm1 }
  4.  
  5.   TForm1 = class(TForm)
  6.     Button1: TButton;
  7.     procedure Button1Click(Sender: TObject);
  8.   private
  9.  
  10.   public
  11.  
  12.   end;
  13.  AFunction = Function(Sender:TList):TList;
  14. var
  15.   Form1: TForm1;
  16.  
  17. implementation
  18.  
  19. {$R *.lfm}
  20. Function Test(Sender:TList):TList;
  21. Begin
  22.   Result := Sender;
  23.   Beep;
  24. end;
  25.  
  26. Function Map(F:Afunction; AList:TList):TList;
  27. Begin
  28.   Result := F(Alist); //All functions must use same prototype..
  29. End;
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.Button1Click(Sender: TObject);
  34. Var
  35.   L:Tlist;
  36. begin
  37.   Map(@Test, L);
  38. end;
  39.  
  40. end.                                  
  41.  

This can only work if all functions past use the same prototype..
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 23, 2019, 11:54:38 am
What is the problem that anonymous functions solve? I don't get it.
In this specific case they allow you to define the function "inline". My example from above (shortened), but with anonymous functions:
Code: Pascal  [Select][+][-]
  1. var
  2.   arr: array of LongInt;
  3.   i: LongInt;
  4. begin
  5.   arr := specialize Map<LongInt>(function(aElem: LongInt): LongInt begin Result := aElem * 2; end, [3, 5, 6, 8]);
  6.   for i in arr do
  7.     Writeln(i);
  8. end.
Due to the syntax of anonymous functions that looks a bit convoluted and once the functionality of anonymous functions is integrated into trunk I want to play around with a simpler syntax like this:
Code: Pascal  [Select][+][-]
  1. arr := specialize Map<LongInt>(lambda (aElem) as aElem * 2, [3, 5, 6, 8]);
This would require type inference for the lambda type and thus a new concept for Pascal so I'm a bit reluctant here, but I'll play around with it nevertheless.

And what anonymous functions allow as well is this:
Code: Pascal  [Select][+][-]
  1. var
  2.   arr: array of LongInt;
  3.   i, fac: LongInt;
  4. begin
  5.   fac := 42; // e.g. retrieved from some file or whatever
  6.   arr := specialize Map<LongInt>(function(aElem: LongInt): LongInt begin Result := aElem * fac; end, [3, 5, 6, 8]);
  7.   for i in arr do
  8.     Writeln(i);
  9. end.

The resulting anonymous function variable can also be passed outside of the scope it was declared in and still be used:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyFunc = function(aArg: LongInt): LongInt is reference;
  3.  
  4. function GetFunc(aArg: LongInt): TMyFunc;
  5. var
  6.   tmp: LongInt;
  7. begin
  8.   tmp := aArg * 2;
  9.   Result := function(aArg: LongInt): LongInt begin Result := aArg * tmp; end;
  10. end;
  11.  
  12. var
  13.   func: TMyFunc;
  14. begin
  15.   func := TMyFunc(2);
  16.   Writeln(func(4));
  17.   Writeln(func(6));
  18.   func := TMyFunc(4);
  19.   Writeln(func(4));
  20.   Writeln(func(6));
  21. end.
This is something that can't be replicated with nested functions outside of very simple cases.
Title: Re: Functional programming in Pascal
Post by: rsz on June 23, 2019, 12:11:54 pm
What is the problem that anonymous functions solve? I don't get it.
If you ever used a functional programming language like Haskell then you will miss them  ;D. They are especially useful for writing async or concise code that doesn't jump (spaghetti) all over the place, so it's easier to maintain. They are also very suited for one off functions, e.g. specific sort functions or a small block of code that you need often inside a single function but which doesn't deserve a place in the namespace because it is too specific.

Something like this?

Code: Pascal  [Select][+][-]
  1. function IsNegative(SomeParam: Integer): Boolean;
  2. var
  3.   SomeVar: Integer;
  4.  
  5.   function Calculate: Boolean;
  6.   begin
  7.     Result := False;
  8.     if SomeVar < 0 then Result := True;
  9.   end;
  10.  
  11. begin
  12.   Result := Calculate;
  13. end;

They copies it from Pascal  ;)
Very cool, I'll be using this until it lands. Do you know if the function gets inlined by fpc automatically, or do I have to declare it as inline?

Due to the syntax of anonymous functions that looks a bit convoluted and once the functionality of anonymous functions is integrated into trunk I want to play around with a simpler syntax like this:
Code: Pascal  [Select][+][-]
  1. arr := specialize Map<LongInt>(lambda (aElem) as aElem * 2, [3, 5, 6, 8]);
This would require type inference for the lambda type and thus a new concept for Pascal so I'm a bit reluctant here, but I'll play around with it nevertheless.
This would be nice if it's possible.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 23, 2019, 12:58:00 pm
PascalDragon, I do understand that you can make your code less readable by fitting everything on a single line. But is there any new functionality?
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 23, 2019, 02:36:57 pm
Didn't you read what I wrote? It allows for functions together with state to be passed around outside of the scope they were declared in. Somewhat like nested functions, but not restricted to their point of declaration.
Title: Re: Functional programming in Pascal
Post by: Zoran on June 23, 2019, 02:46:02 pm
PascalDragon, I do understand that you can make your code less readable by fitting everything on a single line. But is there any new functionality?

Yes, there is.
You obviously missed the point from PascalDragon's examples.

Read his examples more carefully, see in the last example, that you can for instance generate a functions with one parameter, depending on some other parameter (local var from other function, or whatever).

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyFunc = function(aArg: LongInt): LongInt is reference;
  3.  
  4. function GetFunc(aArg: LongInt): TMyFunc;
  5. var
  6.   tmp: LongInt;
  7. begin
  8.   tmp := aArg * 2;
  9.   Result := function(aArg: LongInt): LongInt begin Result := aArg * tmp; end;
  10. end;
  11.  

Actually, I very much agree with you about lesser readability, but yes, there is actually new functionality.
The bad thing is that people will use it all over the place, when not actually needed, which will do nothing else but make the code less readeable.
It should be used sparingly.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 23, 2019, 05:40:29 pm
In your example, what is the actual result? A number, a pointer to a function or something else?

And because I don't understand, I read the Wikipedia article, which states:

Quote from: Wikipedia
Use of anonymous functions is a matter of style. Using them is never the only way to solve a problem; each anonymous function could instead be defined as a named function and called by name.
Title: Re: Functional programming in Pascal
Post by: Almir.Bispo on June 23, 2019, 05:45:15 pm
Please don't let C programmers work on FPC because it can be denify
(Say NO to "modern Dinamics").Pascal til used because is native and typed.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 23, 2019, 08:19:39 pm
Ok. Let's take an example: spawning threads in Java.

A thread in Java is essentially an anonymous (void) function without parameters. It is a procedure, that will run unhindered, do its thing and terminate. Cool. That's the best way to use threads.

But, there is also no immediate interaction. Then again, that's not so bad in Java, as it runs in a VM and all threads and instances are managed (actually, they're all globally available through the JNI). Like in C/C++, you have to give the tread a pointer to an object if you want it to communicate with it. And yes, it does have its own internal state (local vars).

On the other hand, it is of type Runnable (an interface), so you can also make a void function of that type and use that. That's how you can pass that pointer as parameter. If you want more from Java threads, things become complicated.

But you can do all those things already in Object Pascal for a few decades. And much better.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 23, 2019, 11:00:06 pm
Btw, I think it's interesting that I have big problems with finding a job, because HR managers think I cannot do anything because I haven't got the right degree, and my co-workers become really irritated because they have no idea what I'm talking about.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 24, 2019, 09:17:31 am
In your example, what is the actual result? A number, a pointer to a function or something else?
The type of Result in the example is a function reference (see the declaration of TMyFunc). And that function reference can then be called somewhere else. You can think of it like a function or method variable, but with a bit more magic behind the scenes involved.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 24, 2019, 09:22:55 am
Is a function reference different from a function pointer? And is the state that the parameters are already filled?
Title: Re: Functional programming in Pascal
Post by: valdir.marcos on June 24, 2019, 11:05:29 am
PascalDragon, I do understand that you can make your code less readable by fitting everything on a single line. But is there any new functionality?

Didn't you read what I wrote? It allows for functions together with state to be passed around outside of the scope they were declared in. Somewhat like nested functions, but not restricted to their point of declaration.

PascalDragon, I do understand that you can make your code less readable by fitting everything on a single line. But is there any new functionality?
Yes, there is.
You obviously missed the point from PascalDragon's examples.

Read his examples more carefully, see in the last example, that you can for instance generate a functions with one parameter, depending on some other parameter (local var from other function, or whatever).

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyFunc = function(aArg: LongInt): LongInt is reference;
  3.  
  4. function GetFunc(aArg: LongInt): TMyFunc;
  5. var
  6.   tmp: LongInt;
  7. begin
  8.   tmp := aArg * 2;
  9.   Result := function(aArg: LongInt): LongInt begin Result := aArg * tmp; end;
  10. end;

Actually, I very much agree with you about lesser readability, but yes, there is actually new functionality.
The bad thing is that people will use it all over the place, when not actually needed, which will do nothing else but make the code less readeable.
It should be used sparingly.
This feature superficially remembers me of Computer Associate Clipper's Eval(), AEval() and DBEval():
https://www.itlnet.net/programming/program/Reference/c53g01c/ng418f5.html
https://vivaclipper.wordpress.com/2014/01/09/eval/
https://vivaclipper.wordpress.com/2014/01/07/aeval/
https://vivaclipper.wordpress.com/2014/01/17/dbeval/

Three or two decades ago, that feature eased a lot to get the job done for a very few of us, but became a real nightmare in maintenance for many programmers not that high skilled.
The same happened as we over used pre-proccessed syntax instead of CA Clipper's standard one.
https://vivaclipper.files.wordpress.com/2012/11/preprocessor-primer.pdf
Time taught us that the most important and expensive parts of a project life-circle are before (the beginning studies and decisions period) and after (the maintenance period) and not the intensive programming middle activity; today best know as KISS, an acronym for "keep it simple, stupid":
https://en.wikipedia.org/wiki/KISS_principle
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 24, 2019, 01:23:58 pm
Microsoft made .NET 15 years ago, and decided there wasn't enough lock-in. So they came up with the Foundations: Windows Presentation Foundation, Windows Communication Foundation and one more for scheduling that was dropped.

Of those, Communication Foundation decided that a new communication paradigm was in order: instead of only sending data, it would send the whole object over the line. This mostly worked as expected for integers, but strings were different and objects weird. To send an object to another computer, it was decompiled and the source code with the fields was send over the line. It was recompiled and executed at the other side.

This is perfect to lock in people to the Microsoft eco-system, as it will be quite hard to run this on another platform. And it generated lots of hair-raising problems when you had a distributed project or just wanted to communicate with, say, a Unix server. (My boss demanded it.) And debugging it was really hard.

It's the same idea.
Title: Re: Functional programming in Pascal
Post by: 440bx on June 24, 2019, 02:12:56 pm
This feature superficially remembers me of Computer Associate Clipper's Eval(), AEval() and DBEval():
What that "feature" reminds me of is the old COBOL "ALTER" statement which allowed a programmer to alter (pun fully intended) the target of a goto.  Someone figured that a simple "GOTO" wasn't bad enough, with "ALTER" not only the programmer could "GOTO" just about anywhere in the program but the target of the "GOTO" could only be known at run time.   goto-s "al-dente" with lots of marinara sauce (nose bleed) and a thick layer of parmesan (aspirin gewk).   Great stuff :) 

Three or two decades ago, that feature eased a lot to get the job done for a very few of us, but became a real nightmare in maintenance for many programmers not that high skilled.
I always thought that the mark of a highly skilled programmer is the ability to produce code that is easy to maintain for the average programmer. 



Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 24, 2019, 03:16:49 pm
I always thought that the mark of a highly skilled programmer is the ability to produce code that is easy to maintain for the average programmer.
And to make sure all the interfaces are defined and used. Bonus points if that is easier than using the quick-and-dirty way.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 24, 2019, 04:25:49 pm
Btw, about the readability:

A few years ago, I was working on a C# ASP.NET webapp. And the lead designer spend his time with Resharping all the source code. And he turned on all the options. So, when I made some beautiful, readable and maintainable code, he turned it in an unreadable and unmaintainable mess of nested LINQ queries (Lambda functions, or anonymous functions).
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 25, 2019, 09:51:25 am
Is a function reference different from a function pointer? And is the state that the parameters are already filled?
Yes, it's different, because a function pointer is only a single CodePointer. Compare that to a method pointer which is in fact a TMethod containing both a function pointer and the Self pointer. A function reference is again different, because it also contains space for all the captured variables from the outer scope that is kept alive until the last function reference goes out of scope.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 25, 2019, 01:33:53 pm
A pointer to an object instance?
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 26, 2019, 03:24:54 pm
Behind the scenes it's an interface with only an Invoke() method that's implemented by a class instance. This class instance is shared by all anonymous functions of a single scope. Though this is all an implementation detail.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 27, 2019, 09:50:28 pm
Ok, I understand. Thanks for explaining it. I do understand the possibilities.

So, you can store the state and call it later. This is only really useful if it accesses external (outside the instance) data or devices, because otherwise you could have simply stored the result.

With the C# WCF analogues, the main problem was it becoming desynced, most often because one or more of those external memory locations or devices went into a different state between creation and execution. Mutexes don't fix that.

How would you debug them, especially when you pass them to a different process?

Interesting to see how you are implementing this complex feature.
Title: Re: Functional programming in Pascal
Post by: Akira1364 on June 28, 2019, 12:16:44 am
This library: https://github.com/avk959/LGenerics (https://github.com/avk959/LGenerics) is IMO a great example of what can be done with FPC generics, and contains quite a bit of functional-esque stuff for arrays/e.t.c. similar to what's being asked about here.

Behind the scenes it's an interface with only an Invoke() method that's implemented by a class instance. This class instance is shared by all anonymous functions of a single scope. Though this is all an implementation detail.

While I certainly want anonymous functions in FPC sooner than later, I really wish they weren't implemented this way (which not only makes them un-inlinable but also requires heap allocation.)

Hopefully that can be optimized / remedied at some point after the initial implementation is merged, though. Having FPC be able to inline normal procvars would be a nice also.
Title: Re: Functional programming in Pascal
Post by: rsz on June 28, 2019, 12:24:00 am
Btw, about the readability:

A few years ago, I was working on a C# ASP.NET webapp. And the lead designer spend his time with Resharping all the source code. And he turned on all the options. So, when I made some beautiful, readable and maintainable code, he turned it in an unreadable and unmaintainable mess of nested LINQ queries (Lambda functions, or anonymous functions).

Every language feature can be abused, that does not inherently make it bad. Bad programmers will write bad code in any language. I used LINQ at my last job and I know it can make code more readable if done right. Just off the top of my head, you can remove a double nested for loop by using LINQs SelectMany.

I personally don't believe ObjectPascal should become a functional language. That being said, I can see certain language features from functional languages that may make sense to implement. Anonymous functions and type inference are ones that I can think of.

e.g. anonymous function with lambda syntax is clear and concise.
Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<MyObject>
  4. begin
  5.   L.Sort(lambda (a, b) as Result := a.X > b.X);
  6. end;
  7.  

e.g. type inference.
Code: Pascal  [Select][+][-]
  1. generic function DoStuff<T>(a, b: T): T;
  2. begin
  3.   Result := a + b;
  4. end;
  5.  
  6. procedure MyProcedure;
  7. var
  8.   A, B, X: Integer;
  9. begin
  10.   A := 1;
  11.   B := 2;
  12.   X := DoStuff(A, B);  // no need to specialize DoStuff, compiler can infer type and specialize automatically.
  13. end;
  14.  

People always state goto in C as a feature that gets abused, but even goto has legitimate uses ;D. Here is a legitimate example (I don't like C):
Code: C  [Select][+][-]
  1. void myfunc() {
  2.     MyContext a, b, c;
  3.     a = create_context();
  4.     b = create_context();
  5.     c = create_context();
  6.     if(!a) goto end;
  7.     if(!b) goto cleanup_a;
  8.     if(!c) goto cleanup_b;
  9.     // do stuff
  10.     if(do_stuff(a) != 0) {
  11.         // failed!
  12.         goto cleanup;
  13.     }
  14.     // do more stuff
  15. cleanup:
  16. cleanup_c:
  17.     destroy_context(c);
  18. cleanup_b:
  19.     destroy_context(b);
  20. cleanup_a:
  21.     destroy_context(a);
  22. end:
  23. }

Title: Re: Functional programming in Pascal
Post by: Akira1364 on June 28, 2019, 12:57:26 am
e.g. type inference.
Code: Pascal  [Select][+][-]
  1. generic function DoStuff<T>(a, b: T): T;
  2. begin
  3.   Result := a + b;
  4. end;
  5.  
  6. procedure MyProcedure;
  7. var
  8.   A, B, X: Integer;
  9. begin
  10.   A := 1;
  11.   B := 2;
  12.   X := DoStuff(A, B);  // no need to specialize DoStuff, compiler can infer type and specialize automatically.
  13. end;
  14.  

That kind of type inference for generics is actually already fully implemented, BTW. It's currently just waiting on being merged to trunk.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 28, 2019, 01:02:52 am
e.g. anonymous function with lambda syntax is clear and concise.
Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<MyObject>
  4. begin
  5.   L.Sort(lambda (a, b) as Result := a.X > b.X);
  6. end;

What bothers me most about Lambda functions is, that their syntax is entirely different from everything around it. It is a second, embedded language.

Like, you have two vars, a and b, but they're not actual vars. They are like magic. Consider this instead:

Code: Pascal  [Select][+][-]
  1. for s in MyStringList do
  2.   ..

It's clear what is happening here. You might even have to write the enumerator yourself. There is a one-on-one relation.

And in your example, you could simply write:

Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<MyObject>
  4. begin
  5.   L.Sort;
  6. end;

That would have the exact same effect.

Edit: If you REALLY want Lambda functions, do it like this:

Code: Pascal  [Select][+][-]
  1. for s1, s2 in MyStringList step 1 do
  2.   ..
Title: Re: Functional programming in Pascal
Post by: rsz on June 28, 2019, 01:28:57 am
That kind of type inference for generics is actually already fully implemented, BTW. It's currently just waiting on being merged to trunk.

Nice  8-).

What bothers me most about Lambda functions is, that their syntax is entirely different from everything around it. It is a second, embedded language.

Like, you have two vars, a and b, but they're not actual vars. They are like magic. Consider this instead:

I don't quite understand. Imagine the sort function calling another function 'Compare' which takes two arguments, a and b. Under the hood that lambda is no different than a function with 2 parameters, a and b.
I think the syntax with 'lambda' and the 'as' suits pascal, but maybe someone can come up with a better pascal-esque syntax for lambdas that would fit even better into the language?

And in your example, you could simply write:

Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<MyObject>
  4. begin
  5.   L.Sort;
  6. end;

That would have the exact same effect.

It's not the same, I am sorting the list by the member 'X'. I could use a different lambda and sort the List by the member 'Y' or even member 'Z' instead, for example.
Imagine you have a list of 'Person' but you want to sort them by age or by company position:

Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<Person>
  4. begin
  5.   L.Sort(lambda (a, b) as Result := a.Age > b.Age);
  6.   // or
  7.   L.Sort(lambda (a, b) as Result := a.Position > b.Position);
  8. end;
  9.  
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 28, 2019, 02:20:09 am
Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<Person>
  4. begin
  5.   L.Sort(lambda (a, b) as Result := a.Age > b.Age);
  6.   // or
  7.   L.Sort(lambda (a, b) as Result := a.Position > b.Position);
  8. end;
  9.  

Ok. Let's rewrite that:

Code: Pascal  [Select][+][-]
  1. procedure MyProcedure(var L: TMyList<Person>);
  2. begin
  3.   L.Sort('Age');
  4.   // or
  5.   L.Sort('Position');
  6. end;
  7.  
Title: Re: Functional programming in Pascal
Post by: Akira1364 on June 28, 2019, 04:31:21 am
Code: Pascal  [Select][+][-]
  1. procedure MyProcedure(var L: TMyList<Person>);
  2. begin
  3.   L.Sort('Age');
  4.   // or
  5.   L.Sort('Position');
  6. end;
  7.  

What exactly is this supposed to be doing?
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 28, 2019, 09:19:46 am
Behind the scenes it's an interface with only an Invoke() method that's implemented by a class instance. This class instance is shared by all anonymous functions of a single scope. Though this is all an implementation detail.

While I certainly want anonymous functions in FPC sooner than later, I really wish they weren't implemented this way (which not only makes them un-inlinable but also requires heap allocation.)

Hopefully that can be optimized / remedied at some point after the initial implementation is merged, though. Having FPC be able to inline normal procvars would be a nice also.
It is needed for Delphi compatibility as there is code out there that (ab)uses this implementation detail. Also it's necessary for the lifetime management of the state (it *could* be done some other way, but why implement something new if existing functionality can be used).

The inlining is also only useful in a very small number of cases. Most often anonymous functions are passed to other functions/classes (e.g. as the Execute function for TThread) and then you can not inline anymore as you're essentially dealing with function pointers then. In my opinion it's wasted time to add support for inlining anonymous functions.

I think the syntax with 'lambda' and the 'as' suits pascal, but maybe someone can come up with a better pascal-esque syntax for lambdas that would fit even better into the language?
There needs to be some keyword that lets the compiler decide whether it should parse a lambda function or not. I already have enough problems with implementing Delphi generic specialization with their oh so ambiguous syntax (especially as generic types can be overloaded with constants and variables).

Code: Pascal  [Select][+][-]
  1. procedure MyProcedure;
  2. var
  3.   L: MyList; // TMyList<Person>
  4. begin
  5.   L.Sort(lambda (a, b) as Result := a.Age > b.Age);
  6.   // or
  7.   L.Sort(lambda (a, b) as Result := a.Position > b.Position);
  8. end;
  9.  
Please note that the idea is that the right side of the as is an expression, not a statement, so it would be
Code: Pascal  [Select][+][-]
  1. lambda (a, b) as a.Age > a.Age

What bothers me most about Lambda functions is, that their syntax is entirely different from everything around it. It is a second, embedded language.
They consist of Pascal expressions, so what exactly is a second language here?

Like, you have two vars, a and b, but they're not actual vars. They are like magic.
No, they're parameters. It's simply part of the lambda's syntax:

Code: [Select]
LAMBDA::=lambda [(LAMBDABPARAMS)] as EXPR
LAMBDAPARAMS::=LAMBDAPARAM[;LAMBDAPARAMS]
LAMBDAPARAM::=IDENTLIST[:TYPE]
IDENTLIST::=IDENTIFIER[,IDENTIFIER]

Edit: If you REALLY want Lambda functions, do it like this:

Code: Pascal  [Select][+][-]
  1. for s1, s2 in MyStringList step 1 do
  2.   ..
This is not a lambda function. It's seems you still don't get the conceptual idea behind lambdas/anonymous functions.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 28, 2019, 09:45:37 am
Code: Pascal  [Select][+][-]
  1. procedure MyProcedure(var L: TMyList<Person>);
  2. begin
  3.   L.Sort('Age');
  4.   // or
  5.   L.Sort('Position');
  6. end;
  7.  

What exactly is this supposed to be doing?

That is a good question  :D

What is Person? Is it a record or a class? If the latter, are Age and Position fields or properties? And if fields, are they public? So, you need compiler magic to figure out what they are and how to handle them. It is the same with the Lambda functions.

In my example, it is the analogue of the FieldByName function. Or like a named indexer.

Anyway, whatever. The chances of me working in a team on a Free Pascal project are slim, so it doesn't matter to me. I can still write the programs in the way I want.
Title: Re: Functional programming in Pascal
Post by: Akira1364 on June 28, 2019, 03:43:50 pm
It is needed for Delphi compatibility as there is code out there that (ab)uses this implementation detail. Also it's necessary for the lifetime management of the state (it *could* be done some other way, but why implement something new if existing functionality can be used).

The inlining is also only useful in a very small number of cases. Most often anonymous functions are passed to other functions/classes (e.g. as the Execute function for TThread) and then you can not inline anymore as you're essentially dealing with function pointers then. In my opinion it's wasted time to add support for inlining anonymous functions.

I'm fully aware it's required for Delphi compatibility, and I did not mean that it should never work that way.

What I meant was that a function or procedure that takes an anonymous function as an argument, and specifically does not capture state but simply makes use of the return value, is negatively impacted performance-wise if the anonymous function still requires heap allocation no matter what.

The "tmaptest" example you yourself posted in this thread is a perfect example of something that has absolutely no reason to create and destroy a hidden interface instance if implemented with anonymous methods (and indeed would actually have better performance as you wrote it, because while function pointers can't currently be inlined in FPC, they at least also do not allocate anything.)

It is simply not true that there is any legitimate technical reason that function pointers cannot be inlined, though. I can't really think of a compiler for any "native" language other than FPC (and Delphi) that doesn't do so. (I've actually looked into what it would take to implement it in FPC myself, and the only thing that seems to make it difficult at all is the fact that "procvardefs" have an overly-limited amount of information associated with them versus "procdefs".)

For example, here's a small C++ proram showing that both C++11 lambdas and also "classic" function pointers are indeed fully inlineable:
https://godbolt.org/z/BPdOI6 (https://godbolt.org/z/BPdOI6)

Edit:

The same program in Swift: https://godbolt.org/z/72uzPw (https://godbolt.org/z/72uzPw)
And in Rust: https://godbolt.org/z/7h0ssB (https://godbolt.org/z/7h0ssB)
Also in C (with no anonymous function obviously, just the function pointer): https://godbolt.org/z/hX76pQ (https://godbolt.org/z/hX76pQ)
And lastly in D: https://godbolt.org/z/N5o46p (https://godbolt.org/z/N5o46p)

In all cases, no heap allocation is required for the non-capturing anonymous function, and the use of both it as well as the "normal" function pointer is inlined completely.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 29, 2019, 09:59:33 pm
What I meant was that a function or procedure that takes an anonymous function as an argument, and specifically does not capture state but simply makes use of the return value, is negatively impacted performance-wise if the anonymous function still requires heap allocation no matter what.
But the code that gets passed such an anonymous function can not differentiate whether the function got state or not. In fact in one case it can be called with a function reference that has state and in another with one that doesn't. So there is only one way that the code calling the anonymous function can call said function: by treating the function reference as an interface and calling Invoke.
ONLY if the compiler could proof that ONLY anonymous functions without state are passed to that other code could it change the function reference to a mere function pointer.
This restricts the scope of such an optimization quite a bit:
- the code would all need to reside in the implementation section of a unit (or completely inside the main program file)
- or all units are compiled as part of a WPO pass cause as soon as one participating function is part of the interface section the compiler must not do such optimizations as the unit might be used as part of a program where a function with state is passed in

It is simply not true that there is any legitimate technical reason that function pointers cannot be inlined, though. I can't really think of a compiler for any "native" language other than FPC (and Delphi) that doesn't do so. (I've actually looked into what it would take to implement it in FPC myself, and the only thing that seems to make it difficult at all is the fact that "procvardefs" have an overly-limited amount of information associated with them versus "procdefs".)

For example, here's a small C++ proram showing that both C++11 lambdas and also "classic" function pointers are indeed fully inlineable:
https://godbolt.org/z/BPdOI6 (https://godbolt.org/z/BPdOI6)
Yes, there is a technical reason: namely if the compiler can not proof that it can simplify it. Your example is an explicit situation where it would also be possible in FPC (if the compiler would support it). Even your example would however fail if you'd split the functions across compilation units (with Link Time Code Generation or whatever the term is for LLVM disabled).
These examples however are highly restricted (see my points mentioned above). And no, only because C++ compilers are able to do this does not mean that FPC can do this as well, because both languages involve different compilation concepts (we have precompiled units with a defined interface while in C++ you only have header files that are essentially "external" declarations). The unit concept simply prohibits some optimizations, because units can be shared between programs (Visual Studio for example complains if you share the immediate build directory (where the .obj files are put in) is shared between projects, because one project might overwrite the other project's files).

The same program in Swift: https://godbolt.org/z/72uzPw (https://godbolt.org/z/72uzPw)
And in Rust: https://godbolt.org/z/7h0ssB (https://godbolt.org/z/7h0ssB)
Also in C (with no anonymous function obviously, just the function pointer): https://godbolt.org/z/hX76pQ (https://godbolt.org/z/hX76pQ)
And lastly in D: https://godbolt.org/z/N5o46p (https://godbolt.org/z/N5o46p)

In all cases, no heap allocation is required for the non-capturing anonymous function, and the use of both it as well as the "normal" function pointer is inlined completely.
Again same problem as in C++. As soon as you get to more real world examples and also apply the conceptual restrictions of Object Pascal that all breaks apart.
Title: Re: Functional programming in Pascal
Post by: Akira1364 on June 30, 2019, 12:23:48 am
Again same problem as in C++. As soon as you get to more real world examples and also apply the conceptual restrictions of Object Pascal that all breaks apart.

You've made fair enough points here I suppose. However, I don't think any of them really indicate that nobody should ever even attempt to implement any of it. I'm still quite confident that given some amount of API tweaking / unification for "procdefs" and "procvardefs" in FPC that it would really not be very difficult to enable callbacks / function pointers / event handlers / whatever you want to call them to be inlined, at the very least.

The compiler ultimately has to know exactly what it's really calling, so the issue is largely centered around making that information easily accessible to the parts of it that check whether or not an inline-marked method can actually be inlined at all, e.t.c.

I might take another look at getting at least basic support for that up and running myself sometime in the next little while, because it's something I think would have extremely far-reaching end-user benefits.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 30, 2019, 10:47:46 am
As long as you remember the restrictions, feel free to play around with it.  ;)
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 30, 2019, 11:59:27 am
it's something I think would have extremely far-reaching end-user benefits.

Could you elaborate?
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on June 30, 2019, 12:22:03 pm
Btw.

This is not a lambda function. It's seems you still don't get the conceptual idea behind lambdas/anonymous functions.

To you, this is a lambda function:

Code: [Select]
LAMBDA::=lambda [(LAMBDABPARAMS)] as EXPR
LAMBDAPARAMS::=LAMBDAPARAM[;LAMBDAPARAMS]
LAMBDAPARAM::=IDENTLIST[:TYPE]
IDENTLIST::=IDENTIFIER[,IDENTIFIER]

and this an anonymous function:

Behind the scenes it's an interface with only an Invoke() method that's implemented by a class instance. This class instance is shared by all anonymous functions of a single scope. Though this is all an implementation detail.

although the latter possibly also could include a function without a name that is executed immediately. That's already three different definitions for the same concept. Closures are often (at least for Thaddy) seen as something different as well.

To me, it's all different implementations of the same thing: an unnamed block of calculations that is applied to some data that is in scope. I would include enumerators (for..in) here as well. And it's all a variant of the Pascal nested function, often with a different syntax.

In short:

1. Definitions can be implementation-specific, but shouldn't be.
2. Implementing the same thing multiple times with a different syntax as copied from a different language doesn't automatically make it a new or important addition to the language, or allow new functionality.
3. The reason why nested functions in Pascal have a name is simply so you can define them in a correct way. It is the same reason there are no inline variable definitions.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on June 30, 2019, 05:17:47 pm
The Invoke() is an implementation detail. Anonymous functions have the following syntax:

Code: [Select]
ANONFUNC::=FUNCHEADER|PROCHEADER BLOCK
FUNCHEADER::=function[(PARAMLIST)]:TYPE DIRECTIVES
PROCHEADER::=procedure[(PARAMLIST)]:TYPE DIRECTIVES
PARAMLIST::=PARAMDECL[;PARAMLIST]
PARAMDECL::=PARAMIDENTLIST:TYPE
PARAMIDENTLIST::=[var|const|out|constref|] IDENTIFIERLIST
IDENTIFIERLIST::=IDENTIFIER[,IDENTIFIERLIST]

As you can see with a quick example (I'll use the one from above) this is a rather verbose syntax:

Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.     function(aLeft, aRight: TPerson): Boolean
  6.     begin
  7.       Result := aLeft.Age < aRight.Age;
  8.     end
  9.   );
  10. end.

The idea of the lambda syntax to have an alternative, more compact syntax that is still clear for the developer:

Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.     lambda (aLeft, aRight) as aLeft.Age < aRight.Age
  6.  );
  7. end.

Behind the scenes both use the same "reference to X" mechanism that uses a compiler generated interface with a Invoke method.

And enumerators are definitely not part of this. They are types that have a MoveNext method and a Current property that fits to the types of a for x in a statement.

Coroutines will hopefully be able to be mapped to the same mechanism as anonymous functions (or at least the same mechanisms with extensions). But first anonymous functions must finally be finished.

By the way: the syntax for anonymous functions with function and procedure is non negotiatable as that syntax is needed for Delphi compatibility.
Title: Re: Functional programming in Pascal
Post by: lucamar on June 30, 2019, 07:10:04 pm
By the way: the syntax for anonymous functions with function and procedure is non negotiatable as that syntax is needed for Delphi compatibility.

Thank the gods! It may be that I'm actually getting old and becoming fixed-minded but I find that sintax actually ... reassuring. No mistery there, just plain old clothes even if doned in a somewhat new way :D
Title: Re: Functional programming in Pascal
Post by: Zoran on July 01, 2019, 10:29:59 am
In this lambda syntax the parameter types and the result type are not declared explicitely? :o
Only now I noticed that. May I hope it will never get into our language.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on July 02, 2019, 09:23:37 am
In this lambda syntax the parameter types and the result type are not declared explicitely? :o
Only now I noticed that. May I hope it will never get into our language.
Yes, that would require type inference. That's why I said that I'm still sceptical about adding this syntax at all. On the other hand it allows for a much more compact syntax, especially for simple lambda expressions (just compare the two examples I've given).
Title: Re: Functional programming in Pascal
Post by: jamie on July 03, 2019, 02:24:57 am
I wrote a complete script parser language using Variants.

 A single function could return any type the variant could handle.

 Also, many of the input variables were also variants..
 
 Kind of a twisted way of doing things but it got me through..
Title: Re: Functional programming in Pascal
Post by: 440bx on July 03, 2019, 03:22:07 am
In this lambda syntax the parameter types and the result type are not declared explicitely? :o
Only now I noticed that. May I hope it will never get into our language.
Yes, that would require type inference. That's why I said that I'm still sceptical about adding this syntax at all. On the other hand it allows for a much more compact syntax, especially for simple lambda expressions (just compare the two examples I've given).

Just curious... couldn't the example you gave be implemented without having to use type inference ... something along the lines of ...
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      lambda (aLeft, aright : TPerson) : boolean as aLeft.Age < aRight.Age
  6.   );
  7. end.
  8.  
It isn't as concise as the construct you showed but, it seems to be more in tune with the Pascal way.


Title: Re: Functional programming in Pascal
Post by: Zoran on July 03, 2019, 10:00:29 am

Just curious... couldn't the example you gave be implemented without having to use type inference ... something along the lines of ...
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      lambda (aLeft, aright : TPerson) : boolean as aLeft.Age < aRight.Age
  6.   );
  7. end.
  8.  
It isn't as concise as the construct you showed but, it seems to be more in tune with the Pascal way.

Then, what is the problem with:
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      function(aLeft, aright: TPerson): Boolean; begin Result := aLeft.Age < aRight.Age end
  6.   );
  7. end.
  8.  
:)
Title: Re: Functional programming in Pascal
Post by: Thaddy on July 03, 2019, 10:09:22 am
@Zoran

nail on head...  :) :) :) :)

Note similar is already possible with function pointers. In that sense it is syntactic sugar anyway.
Title: Re: Functional programming in Pascal
Post by: 440bx on July 03, 2019, 01:27:20 pm
Then, what is the problem with:
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      function(aLeft, aright: TPerson): Boolean; begin Result := aLeft.Age < aRight.Age end
  6.   );
  7. end.
  8.  
:)
I don't see a problem with it. I think it is perfectly fine.

The lambdas do result in very short constructs but part of how they achieve it is using type inference.  The short and succinct syntax is nice, as long as it doesn't come from minuses such as type inference.   What I showed was something that seemed to strike a balance.


@Thaddy,

I agree, it is mostly syntactic sugar.  I say mostly because, after all, it is a construct that preserves the linearity of the execution flow (something I greatly value.)  A function pointer doesn't achieve that.  With a function pointer, you have to jump somewhere in the code to read whatever function the pointer is referencing.


Title: Re: Functional programming in Pascal
Post by: marcov on July 03, 2019, 01:36:29 pm
(before overuse of anonymous methods, specially for threading, also check this thread:

https://forum.lazarus.freepascal.org/index.php/topic,45832.30.html)
Title: Re: Functional programming in Pascal
Post by: PascalDragon on July 03, 2019, 01:41:43 pm

Just curious... couldn't the example you gave be implemented without having to use type inference ... something along the lines of ...
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      lambda (aLeft, aright : TPerson) : boolean as aLeft.Age < aRight.Age
  6.   );
  7. end.
  8.  
It isn't as concise as the construct you showed but, it seems to be more in tune with the Pascal way.

Then, what is the problem with:
Code: Pascal  [Select][+][-]
  1. var
  2.   l: TList<TPerson>;
  3. begin
  4.   l.Sort(
  5.      function(aLeft, aright: TPerson): Boolean; begin Result := aLeft.Age < aRight.Age end
  6.   );
  7. end.
  8.  
:)
Yes, that's the problem if one does not use type inference: Then there is basically no real difference to the syntax that Delphi has.
Note similar is already possible with function pointers. In that sense it is syntactic sugar anyway.
For that specific example this is true. However with anonymous functions you can also have state attached to the "function pointer" and pass the pointer plus the state (together a "function reference") to some outer scope.
Title: Re: Functional programming in Pascal
Post by: 440bx on July 03, 2019, 01:50:23 pm
(before overuse of anonymous methods, specially for threading, also check this thread:

https://forum.lazarus.freepascal.org/index.php/topic,45927.msg325474.html#msg325474 )
Marco, that thread has nothing to do with anonymous methods and/or threading.
Title: Re: Functional programming in Pascal
Post by: marcov on July 03, 2019, 03:13:14 pm
(before overuse of anonymous methods, specially for threading, also check this thread:

https://forum.lazarus.freepascal.org/index.php/topic,45927.msg325474.html#msg325474 )
Marco, that thread has nothing to do with anonymous methods and/or threading.

Fixed, thanks for the hint. Probably copied the wrong one.
Title: Re: Functional programming in Pascal
Post by: Zoran on July 03, 2019, 03:15:55 pm
Note similar is already possible with function pointers. In that sense it is syntactic sugar anyway.
For that specific example this is true. However with anonymous functions you can also have state attached to the "function pointer" and pass the pointer plus the state (together a "function reference") to some outer scope.

Yes, and state is the new feature that anonymous functions bring. For trivial things, I would rather avoid these, they would just make code less readable.

We will be able to do something like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TDivideByNumerFunction: function(N: Integer): Integer is reference;
  3.  
  4. function CreateDivideByNumberFunction(D: Integer): TDivideByNumberFunction;
  5. begin
  6. // D has to be remembered by the resulting function, so that is its "state", right?
  7.   Result := function (N: Integer): Integer; begin Result := N div D end;
  8. // Result := lambda (N) as N div D; // alternative syntax with type inherence.
  9. end;
  10.  
  11. var
  12.   DivideByTwo, DivideByThree: TDivideByNumberFunction;
  13.  
  14. begin
  15.   DivideByTwo := CreateDivideByNumberFunction(2);
  16.   DivideByThree := CreateDivideByNumberFunction(3);
  17.  
  18. // Now, we have two functions actually, as if we had declared these:
  19. // function DivideByTwo(N: Integer): Integer; begin Result := N div 2 end;
  20. // function DivideByThree(N: Integer): Integer; begin Result := N div 3 end;
  21.  
  22.   Writeln(DivideByTwo(12)); // the output is 6
  23.   Writeln(DivideByThree(12)); // 4
  24.   Writeln(DivideByTwo(17)); // 8
  25.   Writeln(DivideByThree(21)); // 7
  26.  
  27.   Readln;
  28. end.
  29.  

That is some kind of partial specialization. Without generics.

I hope I understood this feature, please correct me if not.
Title: Re: Functional programming in Pascal
Post by: 440bx on July 03, 2019, 03:56:56 pm
... pass the pointer plus the state (together a "function reference") to some outer scope.
scope level jumping/referencing... isn't that the very essence of spaghetti code ?  (not even taking into account the difference timing in variable capture can make.)

I think it's fine and, sometimes even desirable, to have a way to define and use function/procedures inline but, letting references to them leak out of the scope they are defined in, not even Italians make spaghetti that jumps from one plate to another.

It seems to me that, lambas - as currently implemented - are an idea/method, from someone who coded themselves into a corner, who needed someway to get themselves out of the mess they made.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on July 04, 2019, 12:42:43 am
The main use for the state is that you can use it to reference external stuff, like sockets. But their state tends to change over time, so don't wait too long with executing them.

Otherwise, it is far simpler to execute it immediately and return the result.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on July 04, 2019, 09:25:19 am
I hope I understood this feature, please correct me if not.
Yes, you understood it correctly. And yes, the D is essentially the state of the function reference.

... pass the pointer plus the state (together a "function reference") to some outer scope.
scope level jumping/referencing... isn't that the very essence of spaghetti code ?  (not even taking into account the difference timing in variable capture can make.)

I think it's fine and, sometimes even desirable, to have a way to define and use function/procedures inline but, letting references to them leak out of the scope they are defined in, not even Italians make spaghetti that jumps from one plate to another.

It seems to me that, lambas - as currently implemented - are an idea/method, from someone who coded themselves into a corner, who needed someway to get themselves out of the mess they made.
They allow you for example to easily set up a thread without the need to declare a whole class:
Code: Pascal  [Select][+][-]
  1. procedure DoSomethingAsynchronous(aObj: TMyWorkObject; aEvent: TEvent);
  2. begin
  3.   TThread.CreateAnonymousThread(procedure
  4.     begin
  5.       DoSomethingWithWorkObject(aObj);
  6.       aEvent.SetEvent;
  7.     end);
  8. end;
At work - where we use C++ - I use their C++ equivalent very much, because they essentially allow to keep code together that conceptually belongs together (though more often than not I also use them, because C++ does not know nested functions  :-[ )
Title: Re: Functional programming in Pascal
Post by: 440bx on July 04, 2019, 12:02:30 pm
They allow you for example to easily set up a thread without the need to declare a whole class:
Code: Pascal  [Select][+][-]
  1. procedure DoSomethingAsynchronous(aObj: TMyWorkObject; aEvent: TEvent);
  2. begin
  3.   TThread.CreateAnonymousThread(procedure
  4.     begin
  5.       DoSomethingWithWorkObject(aObj);
  6.       aEvent.SetEvent;
  7.     end);
  8. end;
At work - where we use C++ - I use their C++ equivalent very much, because they essentially allow to keep code together that conceptually belongs together (though more often than not I also use them, because C++ does not know nested functions  :-[ )
There is no doubt there are cases, like the example you presented, where that syntax results in something that is clean and easier to maintain than the traditional alternative but, it also opens the door to some rather "poor" constructions. 

I think the fact the C/C++ doesn't know nested functions is one of the primary reasons for their use. Often in ways, I find "questionable"  (to put it kindly.)

Thank you for the example.  It's a nice one.
Title: Re: Functional programming in Pascal
Post by: PascalDragon on July 05, 2019, 09:36:26 am
There is no doubt there are cases, like the example you presented, where that syntax results in something that is clean and easier to maintain than the traditional alternative but, it also opens the door to some rather "poor" constructions.
Any feature opens the door for "poor" constructions. Anonymous functions aren't anything special there. If a feature exists it's going to be abused.
Title: Re: Functional programming in Pascal
Post by: marcov on July 05, 2019, 10:21:18 am
Most uses of anonymous methods are in cross thread usage, where nested functions wouldn't work anyway.

Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on July 05, 2019, 01:02:19 pm
How do you track which threads are still active, and how do you distribute the pointers?
Title: Re: Functional programming in Pascal
Post by: 440bx on July 05, 2019, 01:22:40 pm
Any feature opens the door for "poor" constructions. Anonymous functions aren't anything special there. If a feature exists it's going to be abused.
Yes, that is true but, some constructions lend themselves much better for building atrocities than others.  Lambdas are among them.  The only example I've seen of a nice use of a lambda I can think of, is the one you gave in this thread.  The other uses I've seen go from "I can do that just as cleanly and easily without a lambda" to "another sample of logical epilepsy".

That said, there is one thing I do like about them, that is, it's inline (don't have to go some other place in the source to find out what the thing does.)



Title: Re: Functional programming in Pascal
Post by: marcov on July 05, 2019, 01:37:22 pm
How do you track which threads are still active, and how do you distribute the pointers?

I don't do neither, but have to admit I don't entirely understand what you mean by that.

I have a bunch of threads for odd jobs. (two worker threads for light things, and two threads for storing images(*)). Most of synchronizing goes from the threads that do the actual work to the main loop. Mostly for gui, but some state is kept in the mainthread because the gui makes the most complex use of them.

More regular work goes over queues (only queueing a method that drains the queue), but less common work goes via anonymous methods to avoid having to setup object types and queues etc for them.

IOW I never create threads for such things, I always signal a worker thread that is blocked on a tevent, or the mainthread that handles it as part of the message loop.


(*) mostly to mask latency due to when directories are relatively full, directory housekeeping can stall a write for a second
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on July 05, 2019, 02:54:51 pm
So, you have multiple threads and a queue, the threads do their thing and when they are done, they call a synced method in the main prog, after which they try to read the next job from the queue?
Title: Re: Functional programming in Pascal
Post by: marcov on July 05, 2019, 04:05:24 pm
So, you have multiple threads and a queue, the threads do their thing and when they are done, they call a synced method in the main prog, after which they try to read the next job from the queue?

Yes and no, the mainthread is not the initiator of everything.  Cameras and threads monitoring connections (serial or network) also generate events that feed queues.

But yes, many queues are basic producer/consumers using queues.  Most of these queues have pools and factories attached to them. So if you ask the queue for an empty item, it checks the pool if there are unused items in there and then recycles them, or asks the factory to create them otherwise.

Some of the items in the queues are manually reference counted. The queue-pool-factories complex are (each) generic, and there is a derived form of the generic that handles the base refcounting.

Some threads are fed by a queue also have a tevent that must be signaled to unblock the thread and inspect the queue.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on July 05, 2019, 04:45:42 pm
Are there standard components for that? It's always a lot of work to build.
Title: Re: Functional programming in Pascal
Post by: marcov on July 05, 2019, 05:07:49 pm
Are there standard components for that? It's always a lot of work to build.

No, but the base pool-factory unit is maybe 400 lines. IIRC it is even my opengl demonstration software. (https://forum.lazarus.freepascal.org/index.php/topic,30556.msg194484.html#msg194484).

While I'm allowed to distribute parts of that. (my boss is luckily a realistic about the more "utility" units), it is a bit entangled with the framework, so I can't just copy something compilable from there.

Note that I simply use tthreadlist as base for most queues and pools. For moderate use that is no problem, but if you go to very high amounts of messages passed that might hurt.
Title: Re: Functional programming in Pascal
Post by: SymbolicFrank on July 05, 2019, 05:54:10 pm
Thanks, I take a look.

First I used sockets, but that's a lot of overhead. Then a concurrent queue, but it is still hard to track which threads are alive. Then again, I rarely use a pool, I just create one when I need it.

I prefer a few queues, one for new jobs and one for feedback, but that way you cannot check if a thread is still alive. And with jobs, I tend to have a lot of different ones after a while.
Title: Re: Functional programming in Pascal
Post by: mr-highball on August 01, 2019, 05:12:07 pm
I'm a bit late to this topic  :-[....
But, ezthreads allows for the use of nested procs/standard procs/object procs, is reference counted, captures variables, and offers Await functionality for a single thread/group of threads/all threads which I believe should be able to be used for the OP's original requests without requiring a lambda implementation.

here's a simple "hello world" sample pulled from my tester app
https://github.com/mr-highball/ezthreads/blob/master/test/ezthreads_tester.lpr#L88

Code: Pascal  [Select][+][-]
  1. (*
  2.   a "hello world" of sorts. shows a fairly complete demonstration
  3.   of creating a thread to write hello world to the console
  4. *)
  5. procedure TestHelloWorld;
  6. const
  7.   NAME='hi';
  8. var
  9.   Thread : IEZThread;
  10.  
  11.   procedure Start(Const AThread:IEZThread);
  12.   begin
  13.     WriteLn(AThread[NAME]);
  14.   end;
  15.  
  16. begin
  17.   Thread:=TEZThreadImpl.Create;
  18.  
  19.   Thread
  20.     .AddArg(NAME,'TestHelloWorld::hello world from an ezthread!')
  21.     .Setup(Start)
  22.     .Start;
  23.  
  24.   Await;
  25.  
  26.   WriteLn('TestHelloWorld::success, waiting no longer');
  27. end;
  28.  
Title: Re: Functional programming in Pascal
Post by: mr-highball on August 04, 2019, 07:29:24 am
As a proof of concept I decided to implement the "map" functional method using ezthreads. It makes use of the new method generics, so to try it out you'll have to compile with trunk, but here's a simple example, where the input is a generic string list that gets '_modified' appended to each item

Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. {$ModeSwitch nestedprocvars}
  3.  
  4. program ezthreads_tester_functional;
  5.  
  6. uses
  7.   ezthreads, ezthreads.functional, fgl;
  8.  
  9. procedure TestSimpleList;
  10. var
  11.   LList : TFPGList<String>;
  12.   LResult : IFPGListResult<String>;
  13.   I: Integer;
  14.  
  15.   (*
  16.     method passed into map to perform some simple processing
  17.   *)
  18.   function Start(Const AThread : IEZThread; Const AItem : String) : String;
  19.   begin
  20.     Result := AItem + '_modified';
  21.   end;
  22.  
  23. begin
  24.   //input list with a few entries
  25.   LList := TFPGList<String>.Create;
  26.   LList.Add('hello');
  27.   LList.Add('World');
  28.  
  29.   WriteLn('--Before--');
  30.   for I := 0 to Pred(LList.Count) do
  31.     WriteLn(LList[I]);
  32.  
  33.   //now we should be able to call map with on our list
  34.   LResult := Map<String>(LList, Start);
  35.  
  36.   //await the result before assigning new values to the list
  37.   Await(LResult);
  38.  
  39.   //assign the modified values
  40.   LList.Assign(LResult.Result);
  41.  
  42.   WriteLn('--After--');
  43.   for I := 0 to Pred(LList.Count) do
  44.     WriteLn(LList[I]);
  45.   LList.Free;
  46. end;
  47.  
  48. begin
  49.   TestSimpleList;
  50. end.
  51.  
  52.  
One thing to note is that the processing is done in a separate thread, so Await(IEZThread) is called to wait until it's finished.
Attached is the result after running

here's the link to my tester app if anyone wants to play around with it:
https://github.com/mr-highball/ezthreads/blob/master/test/ezthreads_tester_functional.lpr
TinyPortal © 2005-2018