Does Free Pascal have anonymous functions that you can pass around, e.g. to a sort(compare : function, arr : array) function?
_______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Am 17.10.2011 22:53, schrieb Andrew Pennebaker:
> Does Free Pascal have anonymous functions that you can pass around, e.g. > to a sort(compare : function, arr : array) function? > > If not, does anyone know any hacks to accomplish this? No, FPC does not support this currently and I know no one who plans to support it in the near future. A similiar feature available in the upcoming 2.6 and in trunk are "nested function references". I wrote about that some days ago in this mail: http://www.hu.freepascal.org/lists/fpc-pascal/2011-October/030460.html Regards, Sven _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Andrew Pennebaker
On 17/10/2011 21:53, Andrew Pennebaker wrote:
> Does Free Pascal have anonymous functions that you can pass around, > e.g. to a sort(compare : function, arr : array) function? > > If not, does anyone know any hacks to accomplish this? > > Cheers, > > Andrew Pennebaker www.yellosoft.us <http://www.yellosoft.us> > Yes: //you can declare a type : TMyFunction: function(AMyParam : TType): TOtherType; // then implement: function MyFunctionWithArbitraryName(AMyParam: TType): TOtherType; begin {do something with AMyParam} end; //then (or before, if the TMyFunction is in type declaration section) function AFunctionUsingTMyFunctionAsArgument(AFunction : TMyFunction):TWhateverType; var OtherReturn: TOtherType; AParameterToAFunction : TType; begin {some code} OtherReturn := AFunction(AParameter); {other code} end; // and finally even const MYFUNCTIONCOUNT = 1 ArrayOfTMyFunction = array[0..MYFUNCTIONCOUNT-1] of TMyFunction = (MyFunctionWithArbitraryName); {it's a static array, so match the number of elemets} begin for {declared somewhere global} i := 0 to MYFUNCTIONCOUNT-1 do WhateverReturn := AFunctionUsingTMyFunctionAsArgument(ArrayOfTMyFunction[i](MyParam)); end. (Terms and conditions : this is invoked from /dev/mem, some syntax discrepancies may occur as my attention moved on from this as it obviously worked) I have written a dumb CLI interpreter this way ;) recently. (the function table contains command name as string in a record together with the function and the main procedure looks for the name and executes the arbitrary function when found) TMyFunctionRecord = record CLIName: string; MyFunction : TMyFunction end; const ArrayOfCLIFunctions = array[0..CLIFUNCCOUNT-1] of TMyFunctionRecord = ({...}); var CLIFunctionToExecute : TMyFunction; WhateverReturn : TWhateverType; begin for i := 0 to CLIFUNCCOUNT-1 do if ArrayOfCLIFunctions[i].CLIName = paramstr[1] then begin CLIFunctionToExecute := ArrayOfCLIFunctions[i].MyFunction; break; end; WhateverReturn := AFunctionUsingTMyFunctionAsArgument(CLIFunctionToExecute(MyParam)); end. Also the 'anonymous' functions can be implemented in a separate unit which only exports the /relevant/ ones in its interface section. The obvious limitation / safeguard is : you must use the function of declared type to pass into the function AFunctionUsingTMyFunctionAsArgument(AFunction : TMyFunction) and no other type; (An obvious workaround to that is to use varargs ;) but I did not try that so I can't tell whether that would work) It's not much OOP in action (and may have {obvious for some //not me} performance penalties but oh well. ;) L. _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
[...]
(facepalm) I did not try 'anonymous' function as you asked, oh. Need to learn to read ;) L. _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by el_es
Sokol, I'm writing a function GenArray(generator) that returns a random array populated by calling the generator function. So the return type of GenArray matches "array of" the return type of the generator function, for any generator function.
E.g., GenArray(GenChar) would return a random string. (Except I don't know the syntax for passing function pointers.) Maybe you could fork my code and use templates to achieve this?
Andrew Pennebaker
On Tue, Oct 18, 2011 at 4:57 AM, Lukasz Sokol <[hidden email]> wrote:
_______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
On 10/18/2011 8:10 PM, Andrew Pennebaker wrote:
> Sokol, I'm writing a function GenArray(generator) that returns a random > array populated by calling the generator function. So the return type of > GenArray matches "array of" the return type of the generator function, for > any generator function. > > E.g., GenArray(GenChar) would return a random string. (Except I don't know > the syntax for passing function pointers.) Sven pointed out yesterday that you cannot achieve in FPC what you want to achieve. (I haven't used Delphi in a long time and don't know what exactly anonymous functions are in Delphi, but I strongly tend to trust his word.) You can't get anonymous but only typed "function pointers" (cf. also Lukasz mail, second to last paragraph, plus his short mail). Since you were asking for hacks... By way of satire(!): To bypass the need to explicitly define a function type for each data type, you could give up on types and write a lot of functions like function GenChar : Pointer; function GenString : Pointer; The type of all of those can be given as type TGen = function : Pointer; Generator would have to return an array of Pointer in this scenario. Then, you could hack your way on from there. Please don't mention my name, though. However, if you're just asking about the syntax for procedural types, the "Language reference guide" for FPC 2.4.4, section 3.6 (p. 45) has all the details: http://www.freepascal.org/docs.var > Maybe you could fork my code Out of curiosity: Is that the impersonal "you"? > and use templates to achieve this? Generics are currently for classes only, as chapter 8 (p. 87) of the aforementioned reference guide explains. Best, Roland > On Tue, Oct 18, 2011 at 4:57 AM, Lukasz Sokol <[hidden email]> wrote: > >> On 17/10/2011 21:53, Andrew Pennebaker wrote: >>> Does Free Pascal have anonymous functions that you can pass around, >>> e.g. to a sort(compare : function, arr : array) function? >>> >>> If not, does anyone know any hacks to accomplish this? >>> >>> Cheers, >>> >>> Andrew Pennebaker www.yellosoft.us <http://www.yellosoft.us> >>> >> >> Yes: >> >> //you can declare a type : >> >> TMyFunction: function(AMyParam : TType): TOtherType; >> >> // then implement: >> >> function MyFunctionWithArbitraryName(AMyParam: TType): TOtherType; >> begin >> {do something with AMyParam} >> end; >> >> //then (or before, if the TMyFunction is in type declaration section) >> >> function AFunctionUsingTMyFunctionAsArgument(AFunction : >> TMyFunction):TWhateverType; >> var OtherReturn: TOtherType; >> AParameterToAFunction : TType; >> begin >> {some code} >> OtherReturn := AFunction(AParameter); >> {other code} >> end; >> >> // and finally even >> const MYFUNCTIONCOUNT = 1 >> ArrayOfTMyFunction = array[0..MYFUNCTIONCOUNT-1] of TMyFunction = >> (MyFunctionWithArbitraryName); >> {it's a static array, so >> match the number of elemets} >> >> begin >> for {declared somewhere global} i := 0 to MYFUNCTIONCOUNT-1 do >> WhateverReturn := >> AFunctionUsingTMyFunctionAsArgument(ArrayOfTMyFunction[i](MyParam)); >> >> end. >> >> (Terms and conditions : this is invoked from /dev/mem, some syntax >> discrepancies may occur >> as my attention moved on from this as it obviously worked) >> >> I have written a dumb CLI interpreter this way ;) recently. >> (the function table contains command name as string in a record together >> with the function >> and the main procedure looks for the name and executes the arbitrary >> function when found) >> >> TMyFunctionRecord = record >> CLIName: string; >> MyFunction : TMyFunction >> end; >> >> const ArrayOfCLIFunctions = array[0..CLIFUNCCOUNT-1] of TMyFunctionRecord = >> ({...}); >> >> var CLIFunctionToExecute : TMyFunction; >> WhateverReturn : TWhateverType; >> >> begin >> for i := 0 to CLIFUNCCOUNT-1 do >> if ArrayOfCLIFunctions[i].CLIName = paramstr[1] then >> begin >> CLIFunctionToExecute := ArrayOfCLIFunctions[i].MyFunction; >> break; >> end; >> WhateverReturn := >> AFunctionUsingTMyFunctionAsArgument(CLIFunctionToExecute(MyParam)); >> end. >> >> >> Also the 'anonymous' functions can be implemented in a separate unit which >> only exports the >> /relevant/ ones in its interface section. >> >> The obvious limitation / safeguard is : you must use the function of >> declared type to pass into >> the function AFunctionUsingTMyFunctionAsArgument(AFunction : TMyFunction) >> and no other type; >> (An obvious workaround to that is to use varargs ;) but I did not try that >> so I can't tell >> whether that would work) >> >> It's not much OOP in action (and may have {obvious for some //not me} >> performance penalties but oh well. ;) >> >> L. >> fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Andrew Pennebaker
anonimous types, thrice, generic array concatenation, interpreting
Pascal, anonymous functions... I'm guessing what language you come from. Then, the second question arises: why Pascal, now. Anyway, I think that immersing yourself in some classes and pointer logic could help you find some solutions to your tasks. (I still, anyway, suggest the quick and dirty C way with its macros and includes...) Cheers, A. _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Roland Schäfer
On 18.10.2011 21:27, Roland Schäfer wrote:
> Sven pointed out yesterday that you cannot achieve in FPC what you want > to achieve. (I haven't used Delphi in a long time and don't know what > exactly anonymous functions are in Delphi, but I strongly tend to trust > his word.) You can't get anonymous but only typed "function pointers" > (cf. also Lukasz mail, second to last paragraph, plus his short mail). For anonymous functions you can take a look at Embarcadero's Delphi help here: http://docwiki.embarcadero.com/RADStudio/en/Anonymous_Methods_in_Delphi Regards, Sven _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Roland Schäfer
Schäfer, thanks, that's a lot of practical information. Have you used Haskell QuickCheck? It's amazing that such a strictly typed language can do these sorts of things.
Yes, pointers are probably the only way I can implement this, for now. If at all possible, I'd like to use more idiomatic Pascal code. The only way I managed to write the C port was void* tricks :P but there's only so much you can do without lambdas.
If I decide to use pointers, what's the syntax for accepting a function pointer (unknown type, unknown arity) and calling the function? In C, you have to know the entire function signature and explicitly cast the pointer to that before you can call it. Does Pascal also require this, or can I just accept a pointer and call it as if it were a normal function call?
Finally, does Pascal have syntax for something like Lisp's (apply f args), or Smalltalk's Block valueWithArguments? Once I get forAll to accept function pointers, and call the functions, I need a way to pass the values to another function (again, unknown types, unknown arity).
Andrew Pennebaker
2011/10/18 Roland Schäfer <[hidden email]>
_______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
On 10/18/2011 10:23 PM, Andrew Pennebaker wrote:
> Schäfer, thanks, that's a lot of practical information. Have you used > Haskell QuickCheck In fact, I have, but I don't see the connection. > Yes, pointers are probably the only way I can implement this, for now. If at > all possible, I'd like to use more idiomatic Pascal code. Those sentences form a contradiction. > but there's only so much you can do without lambdas. I saw the l-word coming. > If I decide to use pointers, ... you'll end up on CodeSOD. > what's the syntax for accepting a function > pointer (unknown type, unknown arity) and calling the function? In C, you In Pascal, you use procedural type variables, not function pointers; it makes a conceptual and a practical difference. Procedural type variables have a type (hence the name). You can push them around as untyped Pointers, but you shouldn't. My *joke* was about turning the result type of your functions into Pointer, so all your GenSomething functions are of the same procedural type and can thus be accepted as an argument by your generator... with virtually unusable results. Please, RTM. > In C you have to know the entire function signature and explicitly cast the pointer > to that before you can call it. Does Pascal also require this, or can I just > accept a pointer and call it as if it were a normal function call? If you read the aforementioned reference and meditate about it, you might notice why you don't have to cast procedural type variables in Pascal. Regards, Roland _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
The procedural types doc is informative, thanks for the suggestion.
Eh, much humor will be lost on me. I'm a Pascal newbie, so if you suggest function pointers, even with (satire!) comments, that's what I'll use. Pointers do the job in C, why shouldn't they in Pascal?
There are CodeSOD hacks, and there are necessary hacks. If you don't like the void* trickery in qc, issue a pull request. Andrew Pennebaker
2011/10/18 Roland Schäfer <[hidden email]>
_______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Sven Barth-2
Am 18.10.2011 21:42, schrieb Sven Barth:
> For anonymous functions you can take a look at Embarcadero's Delphi help > here: > http://docwiki.embarcadero.com/RADStudio/en/Anonymous_Methods_in_Delphi The Embarcadero style of anonymous functions does not satify me. myFunc := function(x, y: Integer): Integer begin Result := x + y; end; This is not easier than writing: function Bla(x, y: Integer): Integer begin Result := x + y; end; myfunc := @Bla; I would prefer a style like myfunc := @function(x, y: Integer): Integer Result := x + y; But this makes it hard to read. Of course it would be nice to have anonymous functions or even lambdas (think about integrated languages like LinQ). But maybe Pascals strength of easy readable code gets lost. Michael _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Am 19.10.2011 09:31, schrieb Michael Fuchs:
> I would prefer a style like > > myfunc := @function(x, y: Integer): Integer Result := x + y; And how would you create more complex functions? In Pascal code blocks are started with "begin" and ended with "end". I don't see a reason why anonymous methods should differ here. Also the example given by Embarcadero is a bit boring. The interesting thing about anonymous methods (and nested functions types in FPC) is that they can access variables of the surrounding function (at least I hope that I've understand it correctly that nested function types can do that). E.g. TIntegerFunc = reference to function: Integer; procedure SomeOtherProc(aFunc: TIntegerFunc); ... procedure Foo; var x: Integer; begin x := 42; SomeOtherProc(function: Integer; begin Result := x; end;); end; I haven't tested that, but from the description that should work. The FPC equivalent should be (not tested as well): type TIntegerFunc = function: Integer is nested; procedure SomeOtherProc(aFunc: TIntegerFunc); ... procedure Foo; var x: Integer; function ReturnX: Integer; begin Result := x; end; begin x := 42; SomeOtherProc(@ReturnX); end; Regards, Sven _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Andrew Pennebaker
Hi Andrew,
first of all my /first/ name is Lukasz. Would you /like/ me to use your surname to refer to yourself ? Second you seem to be sending the same message twice in one post (once as plain text the other as HTML) and quoted/printable encoding to make matters even worse. Please teach your (gmail ?) to behave. Third you're top-posting and this is is a no-no to many; We're not your corporate friends who are on top of things by definition, we have our lives and we read from the beginning to refresh on the matter. Fourth, you're leaving too much of the quote below your answer, for what exactly? Save electrons, they are becoming scarce. Fourth and I won't go into technicalities here but what is it exactly you want to achieve by trying to make FPC bend over backwards ? L. On 18/10/2011 19:10, Andrew Pennebaker wrote: [...cut quote...] _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Sven Barth-2
Am 19.10.2011 10:16, schrieb Sven Barth:
>> I would prefer a style like >> >> myfunc := @function(x, y: Integer): Integer Result := x + y; > > And how would you create more complex functions? In Pascal code blocks > are started with "begin" and ended with "end". I don't see a reason why > anonymous methods should differ here. Allow both, like it is already allowed in Pascal: if True then DoSomething; if True then begin DoSomething; DoSomethingMore; Etc; end; If a function only contains one statement it is not a codeblock. Michael _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
In reply to this post by Sven Barth-2
19.10.2011 12:16, Sven Barth пишет:
> E.g. > > TIntegerFunc = reference to function: Integer; > > procedure SomeOtherProc(aFunc: TIntegerFunc); > ... > > procedure Foo; > var > x: Integer; > begin > x := 42; > SomeOtherProc(function: Integer; begin Result := x; end;); > end; > > I haven't tested that, but from the description that should work. > > The FPC equivalent should be (not tested as well): > > type > TIntegerFunc = function: Integer is nested; > > procedure SomeOtherProc(aFunc: TIntegerFunc); > ... > > procedure Foo; > var > x: Integer; > > function ReturnX: Integer; > begin > Result := x; > end; > > begin > x := 42; > SomeOtherProc(@ReturnX); > end; > > Regards, > Sven > _______________________________________________ > fpc-pascal maillist - [hidden email] > http://lists.freepascal.org/mailman/listinfo/fpc-pascal > > But _real_ functional equivalent of Delphi's function reference if COM-interface based functor. F.e. "TMyFuction = reference to function: Integer; " and "IMyFunction = interface function Invoke:Integer; end;" _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Administrator
|
Hmm... I'm not really sure though, but I guess you could make use of "array of const" feature. See the documentation here: http://www.freepascal.org/docs-html/ref/refsu60.html
|
In reply to this post by Sven Barth-2
Am 19.10.2011 10:16, schrieb Sven Barth:
> Am 19.10.2011 09:31, schrieb Michael Fuchs: >> I would prefer a style like >> >> myfunc := @function(x, y: Integer): Integer Result := x + y; > > And how would you create more complex functions? In Pascal code blocks > are started with "begin" and ended with "end". I don't see a reason why > anonymous methods should differ here. > > Also the example given by Embarcadero is a bit boring. The interesting > thing about anonymous methods (and nested functions types in FPC) is > that they can access variables of the surrounding function (at least I > hope that I've understand it correctly that nested function types can do > that). The main difference between anonymous methods and nested functions is that one can return a reference to an anonymous method from a function, the anonymous method can access local variables from that function and even after exiting the function, the variables are still valid. This means that local variables accessed in a anonymous method end up on the heap. However, I fail to see reasonable use cases for anonymous methods _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Practical uses for referencable anonymous functions:
(map f collection) This is the prototypical way to run a function over each element in a collection, returning the results. Example: (map (lambda (x) (+ x 1)) '(1 2 3)) -> (2 3 4) (sort compare collection) When dealing with complex data types, the user may want to implement a custom compare function. (sort (lambda (x y) (- y:employee-id x:employeeid)) (list emp1 emp2 emp3)) -> (emp3 emp1 emp2) (zip f collection1 collection2) Similar to map, zip runs a 2-input function over the elements of the collections, returning the results. (zip (lamba (x y) (+ x y)) '(1 2 3) '(4 5 6)) -> (5 7 9) At first glance, each of these examples may seem pointless. Can't we implement the same behavior without referencing anonymous functions? Yes, we can, but only for a specific function. If you want your database library to allow users to customize sorting, you'll need referencable anonymous functions. If you've used a functional language, you know how powerful these little functions are, and you'll miss them and want to implement them in languages that don't have them. Andrew Pennebaker On Wed, Oct 19, 2011 at 2:04 PM, Florian Klämpfl <[hidden email]> wrote:
_______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Am 19.10.2011 20:23, schrieb Andrew Pennebaker:
> Practical uses for referencable anonymous functions: For such applications one uses procedure variables in pascal. > > (map f collection) > > This is the prototypical way to run a function over each element in a > collection, returning the results. > > Example: > > (map (lambda (x) (+ x 1)) '(1 2 3)) > > -> (2 3 4) > > (sort compare collection) > > When dealing with complex data types, the user may want to implement a > custom compare function. > > (sort (lambda (x y) (- y:employee-id x:employeeid)) (list emp1 emp2 emp3)) > > -> (emp3 emp1 emp2) > > (zip f collection1 collection2) > > Similar to map, zip runs a 2-input function over the elements of the > collections, returning the results. > > (zip (lamba (x y) (+ x y)) '(1 2 3) '(4 5 6)) > > -> (5 7 9) > > At first glance, each of these examples may seem pointless. Can't we > implement the same behavior without referencing anonymous functions? > Yes, we can, but only for a specific function. If you want your database > library to allow users to customize sorting, you'll need referencable > anonymous functions. I still don't see why this cannot be done by procedure variables: one can easily pass a procedure reference to a compare function to any sort library call. The example is far from explaining why it is needed to be able to return a reference to an anonymous method to the outside of the enclosing function. _______________________________________________ fpc-pascal maillist - [hidden email] http://lists.freepascal.org/mailman/listinfo/fpc-pascal |
Free forum by Nabble | Edit this page |