Re: [fpc-devel] Functors
Am 26.12.2021 um 02:16 schrieb Blaise--- via fpc-devel: Important design points: 1) Applying round brackets to instances does not collide with the existing syntax; 2) Naturally, helpers are able to turn helpees into functors; 3) Operator () cannot be applied to types -- that would clash with explicit type conversions; 4) Explicit empty argument lists are required -- unorthogonal to routines and procedural variables, but clarity must win here; 5) {$modeswitch Closures} is required (modeswitch_closures.patch from https://lists.freepascal.org/pipermail/fpc-devel/2021-December/044261.html) -- functors are closure-adjacent in the area of functional programming. The parts that are currently missing: 1) Implicit conversion from functors to method pointers -- should be fairly trivial to implement; 2) Support for generics -- should be straightforward as well; 3) The OPERATOR keyword instead of PROCEDURE/FUNCTION for methods Invoke -- should we choose to require it -- would be somewhat more complicated. Rejected. I've finally managed to integrate your support for function references, more is currently not required. Regards, Sven ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
> On Dec 27, 2021, at 1:44 AM, bla...@blaise.ru wrote: > > So, in your book, introducing a /new/ operator identifier "doesn't present > any new syntax" (and I agree), but semantically allowing the /existing/ > directive DEFAULT to appear in the existing list of method directives is > somehow "a new syntax". You have some interesting definition of "syntax". I was mainly just trying to predict what Sven will say because I've proposed so many things over the recent years. :) "Default" has never been used outside of properties which is why it could be considered "new syntax". As for the idea of a "call operator" that would require "self" to be passed in as the first param (like record management operators) AND "class operator" is not allowed in classes right now so this would limit it to records only. I already tried to get Sven to accept class operators on classes (for some operators only) and this was rejected. Given that I would say this would indeed go the way of properties since it's a form of aliasing or re-routing: property Invoke: T read DoInvoke; default; However I have already made a patch (nearly finished) for a "default record property" which does something similar to this (it's meant for smart pointers like how C++ overloads the -> operator) and even though Sven did seem behind the idea I don't see he's been willing to follow up on it. Not sure what this means for the feature or if it will ever be accepted. https://gitlab.com/genericptr/free-pascal/-/tree/default_record_property So if there is now a default method it would confound this idea even further and it's already not clear if FPC will accept this kind of thing. I'm not saying any of this to discourage the idea though since I think it's interesting. Regards, Ryan Joseph ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 27.12.2021 3:14, Martin Frb wrote: it enables you to skip writing the name of the method that is called? anything you can do with it, you can already do without it I am truly flabbergasted at how this could possibly be unclear still. Michael have already asked exactly the same questions in the first reply to this thread. Allow me to quote my response: On 26.12.2021 15:40, Blaise--- via fpc-devel wrote: On 26.12.2021 11:50, Michael Van Canneyt via fpc-devel wrote: As I see it, it's just shorthand syntax to allow skipping the name 'Invoke'. None of what is shown below cannot be handled by ordinary methods Well, yes. This is what irritates me, if this is a feature, it should enable me to do something that otherwise would have been at least some little bit more complex. I shall go tell Default Array Property and Operator Add that, according to Martin Frb, they have been demoted from Features, because "anything he can do with you, he can already do without you" and because they need to be a "little bit more complex". default method @Michael Van Canneyt: And I rest my case. /You/ never called them "default methods", but, merely based on the DEFAULT keyword and syntax similarity, users have already started calling them that -- exactly as I predicted. Then, they google "default methods" and they find "default methods in interfaces" for Java and C# -- a completely unrelated feature. Yes, the DEFAULT keyword is lying around, readily available, but is it worth the confusion of not just inventing a new term for a concept that already has an established name (cf. google "call operator overload"), but overloading the established term with an unrelated meaning? -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 27/12/2021 00:18, bla...@blaise.ru wrote: On 27.12.2021 0:57, Martin Frb via fpc-devel wrote: writeln( aC(33) ); aC('hello'); the above examples are probably intended to show the syntax. But not indented to show any useful application? The test you quoted demonstrates what is already possible, syntactically and semantically, using a simple patch I have extracted from my implementation of Closures. No judgment, but just checking if I missed anything... From the above examples, it then is a shortcut? "shortcut" in the sense that it enables you to skip writing the name of the method that is called? "shortcut" in the sense, that anything you can do with it, you can already do without it (and without changing the structure of your code, or adding massive amounts of code)? This is what irritates me, if this is a feature, it should enable me to do something that otherwise would have been at least some little bit more complex. Functor and class are (as I understand you) equal in: - you need to create / destroy them. - you can store data / add (none default) methods But different in (and only in?), that a functor has a default method. Your way to describe it "it can be passed as a function" or "behaves like a routine" does not add anything new. A method / method-reference can do exactly all of that. As I write this I feel, that I really must be missing some point... I just don't see where. (Sorry for the very repetitive questioning about / just wanted to make sure I highlight every angle of my current understanding) a functor is meant to be ref-counted A functor is a non-routine entity ("object") that behaves like a routine. Meaning: you can invoke it like a routine (via an overloaded "operator ()"), you can store it like a routine (in a method pointer). That is all, it is quite a simple feature, really. RECORD, OBJECT, CLASS, and INTERFACE instances are supported as such entities. With these entities, you manage memory as usual. So basically type TInvoke = procedure of object; TFunctor = class procedure Invoke; end; operator := (a: TFunctor): TInvoke; begin result := TInvoke(@a.Invoke); end; Only - I don't need to write the operator by hand, and therefore do not need to update the name "invoke" if I change it in the class? - the above only works in assignments, *not* for directly calling "MyFunctor();" (which is where it is a shortcut, to leave out the method name) ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 27.12.2021 0:57, Martin Frb via fpc-devel wrote: writeln( aC(33) ); aC('hello'); the above examples are probably intended to show the syntax. But not indented to show any useful application? The test you quoted demonstrates what is already possible, syntactically and semantically, using a simple patch I have extracted from my implementation of Closures. The "missing parts" list recognises that a better syntax for the operator declaration is needed, and suggests the keyword OPERATOR. A better syntax was the major point of the discussion thus far. then what in "functor" is a new feature (rather than shortcut) a functor is meant to be ref-counted A functor is a non-routine entity ("object") that behaves like a routine. Meaning: you can invoke it like a routine (via an overloaded "operator ()"), you can store it like a routine (in a method pointer). That is all, it is quite a simple feature, really. RECORD, OBJECT, CLASS, and INTERFACE instances are supported as such entities. With these entities, you manage memory as usual. -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 27.12.2021 0:03, Michael Van Canneyt via fpc-devel wrote: On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: On 26.12.2021 19:33, Michael Van Canneyt via fpc-devel wrote: On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: following your reasoning, the same should be said about overloading most of the operators: "why is aC+aD better than aC.Add(aD)"? No. Operators are clearly expressions. ac.Add(aD) is a statement. How can you tell just from that? :) If Add is a function (which is clearly implied by the comparison with the operator Add), then "aC.Add(aD)" is an expression, just like "aC+aD". Well: you write it: IF. With aC+aD there is no IF, it's an expression. Clear. I see. I misunderstood your original response. Turns out, you ran with the narrowest possible interpretation of my question, which was the condensed version of the following: Given two statements: 1) A := B + C; and 2) A := B.Add(C); provided they are semantically equivalent -- they both invoke the method Add on B with the argument C and store the result in A -- which one do you prefer syntactically? Let me offer another comparison and rephrase my quote: following your reasoning, the same should be said about default array properties: "why is aC[42] better than aC.Items[42]"? I would not have introduced this. Delphi has. But was it, in your book, a bad design decision, violating the principles that you outlined earlier? default interface methods a dedicated directive method aliasing I think you're getting carried away a little ;-) Let's stick to the topic at hand I believe we are firmly on topic. You are criticising Borland for bad design decisions; yet, ironically, you seem reluctant to consider as much as possible beforehand for this feature, including other possible features and how the subj should fit with them. I am a fan of KISS principle. Hence reusing default, because it is already a keyword. Merely FTR: OPERATOR is already a keyword as well. Not employed as directive, but still a non-reserved keyword. Adding the feature is OK, but let's try to keep impact minimal. For me this means, reuse 'default' keyword and add an operator. If you agree on the operator, what do we need the DEFAULT for, especially if we are "to keep impact minimal"? Alternative designations, be it a directive or routine aliasing, can be introduced later. -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 26/12/2021 02:16, Blaise--- via fpc-devel wrote: I propose that the support for https://en.wikipedia.org/wiki/Function_object be added to the FPC. ... begin aC := C.Create; writeln( aC(33) ); aC('hello'); I might be barging in a bit late. But (within the given set that we have / not within the set of any other language): Is this a new feature? Or a shortcut? Or maybe more precise, how much of it is new. To start with, the above examples are probably intended to show the syntax. But not indented to show any useful application? Lets go to the example (I think from the wiki). Passing the functor, instead of a function ref: MyList.Sort( C.Create('sort_field') ); This is (mostly) a shortcut for MyList.Sort( @C.Create('sort_field').Invoke ); 2 things are different in the above. 1) The instance must be ref-counted, and free itself => or you get a mem leak. That part could be regarded as a new feature. Well, actually, my understanding is that feature comes under the name of "smart pointer" ? (but I may be wrong on that one) 2) Acts as a shortcut. - removes the "@" which in mode objfpc, really belongs to passing on a reference to something that can be called. Yes, if it were a class (and therefore a ref by itself), it would not be needed, but it is a functor, something that can be called. - removes the name of the called method. Actually a pity. Typing out the name, means the ability to chose a diff method, for diff purposes. So based on this, and - if "smart pointer" does bring the required ref-count, - if we do not want to duplicate "smart pointer" then what in "functor" is a new feature (rather than shortcut), compared to the feature already on offer (or under development)? -- One more question. If I am right in the assumption, that a functor is meant to be ref-counted, then by which criteria is the distinction between "functor" and "class" made. In the example a "functor" is declared as a "class". But a class is not ref-counted. And changing that for every class is not an option, since existing code would not work with this. So then, how would the compiler know, that a "class" is a "functor" and ref counted? - Because it has an "invoke" function (original suggestion)? (that clashes with existing code) - Because it has a function that has a "default" modifier? That would be possible, but I think it is not explicit enough. A simple "default" somewhere on one function, hidden in a list of who knows how many functions? So then, the ref-count would need to go as a modifier to the "class" itself. Not sure how smart pointers (when used for classes) are to be declared If I missed anything in the proposal that differs majorly from my understanding, then sorry about the noise. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: On 26.12.2021 19:33, Michael Van Canneyt via fpc-devel wrote: On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: On 26.12.2021 11:50, Michael Van Canneyt via fpc-devel wrote: None of what is shown below cannot be handled by ordinary methods Well, yes. But, following your reasoning, the same should be said about overloading most of the operators: "why is aC+aD better than aC.Add(aD)"? No. Operators are clearly expressions. ac.Add(aD) is a statement. How can you tell just from that? :) If Add is a function (which is clearly implied by the comparison with the operator Add), then "aC.Add(aD)" is an expression, just like "aC+aD". Well: you write it: IF. With aC+aD there is no IF, it's an expression. Clear. Let me offer another comparison and rephrase my quote: following your reasoning, the same should be said about default array properties: "why is aC[42] better than aC.Items[42]"? I would not have introduced this. Delphi has. with the modern Object Pascal as implemented by DCC and FPC? I would reject your proposal using Invoke. Let us be precise here. You mean "procedure/function Invoke", right? Because "operator Invoke" (which is part of my proposal) would not clash with other entities named Invoke. procedure/function invoke. How the operator is called, I honestly don't care, although I would be more in favour of call. Reusing "default" As I wrote in my response to Ryan, I actually quite like that, except for one reservation: should the support for default interface methods be ever added to FPC, the DEFAULT directive would fit there better, IMO. A dedicated directive would be fine with me, and it could be used to designate any compatible method as any operator: ---8<--- type R = record procedure Foo(...); operator (); function Bar(const A, B: R): R; operator +; end; ---8<--- How about another approach, albeit more complex: does FPC support method aliasing? Personally, many times, I wished DCC had that. ---8<--- type R = record procedure Foo(...); operator () = Foo; // routine alias // OR: operator Invoke = Foo; end; ---8<--- Routine aliasing is a more general (hence, more useful) feature on its own; and with it, even inherited methods can be designated as operators in derived classes. I think you're getting carried away a little ;-) Let's stick to the topic at hand: I am a fan of KISS principle. Hence reusing default, because it is already a keyword. For every additional keyword, we need to change umpteen routines in various places. So let's keep that to a minimum. So I repeat my point of view: Adding the feature is OK, but let's try to keep impact minimal. For me this means, reuse 'default' keyword and add an operator. What the name is (call, invoke) does not matter for me... Michael. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 26.12.2021 23:47, Blaise--- via fpc-devel wrote: ---8<--- type R = record procedure Foo(...); operator (); function Bar(const A, B: R): R; operator +; end; ---8<--- Made a blunder there, sorry: for Bar, it should either be function Bar(const Other: R): R; operator +; or class function Bar(const A, B: R): R; static; operator +; -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 26.12.2021 19:33, Michael Van Canneyt via fpc-devel wrote: On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: On 26.12.2021 11:50, Michael Van Canneyt via fpc-devel wrote: None of what is shown below cannot be handled by ordinary methods Well, yes. But, following your reasoning, the same should be said about overloading most of the operators: "why is aC+aD better than aC.Add(aD)"? No. Operators are clearly expressions. ac.Add(aD) is a statement. How can you tell just from that? :) If Add is a function (which is clearly implied by the comparison with the operator Add), then "aC.Add(aD)" is an expression, just like "aC+aD". Let me offer another comparison and rephrase my quote: following your reasoning, the same should be said about default array properties: "why is aC[42] better than aC.Items[42]"? It should be less confusing if you think of aC as a routine (which is the point of Functors). Would aCallback(33) look less confusing? But it is not a routine. It is a class, and it is declared as one. Pascal is very explicit about the constructs and classes it uses. It's a 'Strongly typed language' for a reason. Blurring the line between functions and classes goes against this, in my opinion. Imagine it is 1995, Delphi introduces default array properties, and my response is: "But a class is not an array. It is a class, and it is declared as one. Blurring the line between arrays and classes goes against Pascal being a 'Strongly typed language', in my opinion." Even further: "What is this thing -- array property?! It is clearly invoking methods behind its back! A method call should not be masquerading as array indexing! It is not BASIC, Pascal has different brackets for calls and array indexing for a reason! I understand when you say that you do not find functors useful; that is the matter of personal opinion and style. But to say that they are inconsistent with the modern Object Pascal as implemented by DCC and FPC? I would reject your proposal using Invoke. Let us be precise here. You mean "procedure/function Invoke", right? Because "operator Invoke" (which is part of my proposal) would not clash with other entities named Invoke. Reusing "default" As I wrote in my response to Ryan, I actually quite like that, except for one reservation: should the support for default interface methods be ever added to FPC, the DEFAULT directive would fit there better, IMO. A dedicated directive would be fine with me, and it could be used to designate any compatible method as any operator: ---8<--- type R = record procedure Foo(...); operator (); function Bar(const A, B: R): R; operator +; end; ---8<--- How about another approach, albeit more complex: does FPC support method aliasing? Personally, many times, I wished DCC had that. ---8<--- type R = record procedure Foo(...); operator () = Foo; // routine alias // OR: operator Invoke = Foo; end; ---8<--- Routine aliasing is a more general (hence, more useful) feature on its own; and with it, even inherited methods can be designated as operators in derived classes. -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 26.12.2021 17:40, Ryan Joseph via fpc-devel wrote: I'm 99% certain using the method name "Invoke" would be rejected on the grounds of backwards compatibility. Not to argue for the "procedure/function Invoke" syntax, but it hardly breaks backward compatibility. Only in the purest non-practical sense: "I maintain this code that always used to be rejected and suddenly it gets compiled". Adding a new syntax using "default" like "function Add9(const N: Integer): Integer; default;" That is pretty much the /old/ syntax. I agree with Michael that it would mirror default properties nicely, but: 1) Are we most likely to name that "default method" or "call operator"? 2) We now have proper operator overloading; I would even argue that, were it added to Object Pascal much earlier, we would have had "operator Index" instead of default array properties; 3) I would rather reserve the DEFAULT directive on methods for default interface methods: ---8<--- type I = interface procedure Foo; default; // the above, IMO, looks better than // procedure Foo; not abstract; end; procedure I.Foo; begin { default implementation } end; ---8<--- class operator Call(); Again, the proposal is: ---8<--- type R = record operator Invoke(...)...; end; ---8<--- Specifically: A) without CLASS: 1) Having to declare Self manually would be ridiculous; 2) In interfaces, that method should be instance-virtual. B) The name Invoke is required for compatibility with DCC. Granted, for "operator", some hackery would still be required for 100% DCC compatibility: ---8<--- type MethRef = reference to procedure; type C = class(TInterfacedObject, MethRef) // This needs to map to _operator_ MethRef.Invoke procedure Invoke; end; ---8<--- it doesn't present any new syntax So, in your book, introducing a /new/ operator identifier "doesn't present any new syntax" (and I agree), but semantically allowing the /existing/ directive DEFAULT to appear in the existing list of method directives is somehow "a new syntax". You have some interesting definition of "syntax". -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: On 26.12.2021 11:50, Michael Van Canneyt via fpc-devel wrote: Please explain what's the point or benefit of this. Do you mean "in general", or "specifically to FPC"? A) In general: I reckon that the article covers the matter quite well; I have nothing to add. B) Specifically for FPC: it is up to the users to decide whether they find this useful; I am impartial. Specifically to FPC. Personally for me, specifically for FPC, the points are: 1) A variant of that patch is already a part of the Closures -- it makes sense to expose that to the users and get another feature (Functors) for "half the price"; 2) Orthogonality -- I would rather implement a proper feature instead of employing a specific "hack" for a single case (translating an interface reference into a call). As I see it, it's just shorthand syntax to allow skipping the name 'Invoke'. None of what is shown below cannot be handled by ordinary methods Well, yes. But, following your reasoning, the same should be said about overloading most of the operators: "why is aC+aD better than aC.Add(aD)"? No. Operators are clearly expressions. ac.Add(aD) is a statement. I find the resulting code aC(33) more confusing than anything else It should be less confusing if you think of aC as a routine (which is the point of Functors). Would aCallback(33) look less confusing? But it is not a routine. It is a class, and it is declared as one. Pascal is very explicit about the constructs and classes it uses. It's a 'Strongly typed language' for a reason. Blurring the line between functions and classes goes against this, in my opinion. I think the idea is overly complicated and can be achieved simply by the mechanism used by default array properties: function Add9(const N: Integer): Integer; default; That could be an alternative /syntax/ for denoting the special method, but I do not see how that would "uncomplicate" the /idea/. To me, that is exactly the same idea with the same level of complication. I was strictly speaking of syntax. The idea: I personally don't see the use, but that does not mean I would torpedo the idea. Likewise, conceptually, "default array properties" /is/ "operator []" with a clunkier syntax and a benefit of being able to access such property by name. We clearly disagree on what constitutes a clunkier syntax. I would reject your proposal using Invoke. Reusing "default" - it's just reusing an existing keyword as another modifier for methods. The impact on syntax is negligible. It also leaves you free to choose your identifiers. For backwards compatibility alone the use of Invoke is a bad idea, specially since the RTTI invoke method is prominently a part of RTTI. As a second option the "call operator" would be acceptable. So from my perspective these are OK (one or both): - Allow use of default - Introduce 'Call' operator. For me, the use of a specific method name is not acceptable for backwards compatibility. Michael. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On Sun, 26 Dec 2021, Ryan Joseph via fpc-devel wrote: On Dec 26, 2021, at 3:50 PM, Michael Van Canneyt via fpc-devel wrote: I think the idea of using a fixed member identifier for special purposes is really stupid design. I'll never forgive Embarcadero their 'GetEnumerator' idea... I'm 99% certain using the method name "Invoke" would be rejected on the grounds of backwards compatibility. Adding a new syntax using "default" like "function Add9(const N: Integer): Integer; default;" would probably be rejected on the grounds that it's new syntax alone (that is default is only used with properties currently). Why ? I'm proposing it. As far as I'm concerned, it is the way to go. You could use it to easily promote a method to default on existing calls. Michael. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
> On Dec 26, 2021, at 3:50 PM, Michael Van Canneyt via fpc-devel > wrote: > > Please explain what's the point or benefit of this. To aid in the usage of classes that have the sole intent of being called. Surely though the compiler team will say this is not *needed* and can be achieved using normal method calls. Question to Blaise, how is this adjacent to closures? I just see it as an operator overload, i.e. how C++ allows to overload the () operator. I will also say In my attempt to make a "default property" which could be used for smart pointers Sven specially said absolutely no to the idea of overloading the "." operator so I tend to think this idea won't go far. Regards, Ryan Joseph ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
> On Dec 26, 2021, at 3:50 PM, Michael Van Canneyt via fpc-devel > wrote: > > I think the idea of using a fixed member identifier for special purposes is > really > stupid design. I'll never forgive Embarcadero their 'GetEnumerator' idea... I'm 99% certain using the method name "Invoke" would be rejected on the grounds of backwards compatibility. Adding a new syntax using "default" like "function Add9(const N: Integer): Integer; default;" would probably be rejected on the grounds that it's new syntax alone (that is default is only used with properties currently). I would say an operator would be most likely to be accepted, such as: class operator Call(); because it could be overloaded easily and doesn't present any new syntax. Personally I like this idea but I tend to favor new syntaxes. Regards, Ryan Joseph ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On 26.12.2021 11:50, Michael Van Canneyt via fpc-devel wrote: Please explain what's the point or benefit of this. Do you mean "in general", or "specifically to FPC"? A) In general: I reckon that the article covers the matter quite well; I have nothing to add. B) Specifically for FPC: it is up to the users to decide whether they find this useful; I am impartial. Personally for me, specifically for FPC, the points are: 1) A variant of that patch is already a part of the Closures -- it makes sense to expose that to the users and get another feature (Functors) for "half the price"; 2) Orthogonality -- I would rather implement a proper feature instead of employing a specific "hack" for a single case (translating an interface reference into a call). As I see it, it's just shorthand syntax to allow skipping the name 'Invoke'. None of what is shown below cannot be handled by ordinary methods Well, yes. But, following your reasoning, the same should be said about overloading most of the operators: "why is aC+aD better than aC.Add(aD)"? I find the resulting code aC(33) more confusing than anything else It should be less confusing if you think of aC as a routine (which is the point of Functors). Would aCallback(33) look less confusing? I think the idea is overly complicated and can be achieved simply by the mechanism used by default array properties: function Add9(const N: Integer): Integer; default; That could be an alternative /syntax/ for denoting the special method, but I do not see how that would "uncomplicate" the /idea/. To me, that is exactly the same idea with the same level of complication. Likewise, conceptually, "default array properties" /is/ "operator []" with a clunkier syntax and a benefit of being able to access such property by name. I think the idea of using a fixed member identifier for special purposes is really stupid design. I would agree in general, but you did see that using the OPERATOR keyword is part of the proposal, right? "operator Add" is not necessarily worse than "operator +". -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
Re: [fpc-devel] Functors
On Sun, 26 Dec 2021, Blaise--- via fpc-devel wrote: I propose that the support for https://en.wikipedia.org/wiki/Function_object be added to the FPC. Please explain what's the point or benefit of this. None of what is shown below cannot be handled by ordinary methods, and I find the resulting code aC(33) more confusing than anything else. As I see it, it's just shorthand syntax to allow skipping the name 'Invoke'. If that is all it is, then I think the idea is overly complicated and can be achieved simply by the mechanism used by default array properties: To reuse your example: Type C = class(TObject) function Add9(const N: Integer): Integer; default; end; var aC : C; begin aC:=C.Create; Writeln(ac(33)); end; I think the idea of using a fixed member identifier for special purposes is really stupid design. I'll never forgive Embarcadero their 'GetEnumerator' idea... Michael. A subset of such functionality already existed as a part of my implementation of closures, so I extended that part to implement the core feature for allowing functors -- overloading of the call operator: when round brackets are applied to an instance of a record, object/class, or interface type, they are translated into a call to the method Invoke of that instance. The attached proof-of-concept functors-1.patch allows the following test case to be compiled: ---8<--- type I = interface procedure Invoke; end; type C = class(TInterfacedObject, I) class function Invoke(const N: Integer): Integer; overload; procedure Invoke; overload; end; class function C.Invoke(const N: Integer): Integer; begin result := N + 9 end; procedure C.Invoke; begin writeln(ClassName, '.Invoke') end; type H = class helper for C procedure Invoke(const S: string); overload; end; procedure H.Invoke(const S: string); begin writeln('H.Invoke("', S, '")') end; var aC: C; var anI: I; begin aC := C.Create; writeln( aC(33) ); aC('hello'); anI := aC; anI() end. ---8<--- Important design points: 1) Applying round brackets to instances does not collide with the existing syntax; 2) Naturally, helpers are able to turn helpees into functors; 3) Operator () cannot be applied to types -- that would clash with explicit type conversions; 4) Explicit empty argument lists are required -- unorthogonal to routines and procedural variables, but clarity must win here; 5) {$modeswitch Closures} is required (modeswitch_closures.patch from https://lists.freepascal.org/pipermail/fpc-devel/2021-December/044261.html) -- functors are closure-adjacent in the area of functional programming. The parts that are currently missing: 1) Implicit conversion from functors to method pointers -- should be fairly trivial to implement; 2) Support for generics -- should be straightforward as well; 3) The OPERATOR keyword instead of PROCEDURE/FUNCTION for methods Invoke -- should we choose to require it -- would be somewhat more complicated. -- βþ ___ fpc-devel maillist - fpc-devel@lists.freepascal.org https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
[fpc-devel] Functors
I propose that the support for https://en.wikipedia.org/wiki/Function_object be added to the FPC. A subset of such functionality already existed as a part of my implementation of closures, so I extended that part to implement the core feature for allowing functors -- overloading of the call operator: when round brackets are applied to an instance of a record, object/class, or interface type, they are translated into a call to the method Invoke of that instance. The attached proof-of-concept functors-1.patch allows the following test case to be compiled: ---8<--- type I = interface procedure Invoke; end; type C = class(TInterfacedObject, I) class function Invoke(const N: Integer): Integer; overload; procedure Invoke; overload; end; class function C.Invoke(const N: Integer): Integer; begin result := N + 9 end; procedure C.Invoke; begin writeln(ClassName, '.Invoke') end; type H = class helper for C procedure Invoke(const S: string); overload; end; procedure H.Invoke(const S: string); begin writeln('H.Invoke("', S, '")') end; var aC: C; var anI: I; begin aC := C.Create; writeln( aC(33) ); aC('hello'); anI := aC; anI() end. ---8<--- Important design points: 1) Applying round brackets to instances does not collide with the existing syntax; 2) Naturally, helpers are able to turn helpees into functors; 3) Operator () cannot be applied to types -- that would clash with explicit type conversions; 4) Explicit empty argument lists are required -- unorthogonal to routines and procedural variables, but clarity must win here; 5) {$modeswitch Closures} is required (modeswitch_closures.patch from https://lists.freepascal.org/pipermail/fpc-devel/2021-December/044261.html) -- functors are closure-adjacent in the area of functional programming. The parts that are currently missing: 1) Implicit conversion from functors to method pointers -- should be fairly trivial to implement; 2) Support for generics -- should be straightforward as well; 3) The OPERATOR keyword instead of PROCEDURE/FUNCTION for methods Invoke -- should we choose to require it -- would be somewhat more complicated. -- βþ # HG changeset patch # User Blaise.ru # Date 1640402948 -10800 # Sat Dec 25 06:29:08 2021 +0300 + Functors: applying round brackets to instances calls their method Invoke diff -r 3ecaef5e9a49 -r 0ac7231ddc94 pexpr.pas --- a/pexpr.pas Sat Dec 25 21:36:11 2021 +0300 +++ b/pexpr.pas Sat Dec 25 06:29:08 2021 +0300 @@ -2762,45 +2762,73 @@ else begin - { is this a procedure variable ? } - if assigned(p1.resultdef) and - (p1.resultdef.typ=procvardef) then -begin - { Typenode for typecasting or expecting a procvar } - if (p1.nodetype=typen) or - ( - assigned(getprocvardef) and - equal_defs(p1.resultdef,getprocvardef) - ) then + if assigned(p1.resultdef) then +case p1.resultdef.typ of + { a procedural variable } + procvardef: begin - if try_to_consume(_LKLAMMER) then + { Typenode for typecasting or expecting a procvar } + if (p1.nodetype=typen) or + ( + assigned(getprocvardef) and + equal_defs(p1.resultdef,getprocvardef) + ) then begin - p1:=comp_expr([ef_accept_equal]); - consume(_RKLAMMER); - p1:=ctypeconvnode.create_explicit(p1,p1.resultdef); + if try_to_consume(_LKLAMMER) then +begin + p1:=comp_expr([ef_accept_equal]); + consume(_RKLAMMER); + p1:=ctypeconvnode.create_explicit(p1,p1.resultdef); +end + else +again:=false end else -again:=false -end +begin + if try_to_consume(_LKLAMMER) then +begin + p2:=parse_paras(false,false,_RKLAMMER); + consume(_RKLAMMER); + p1:=ccallnode.create_procvar(p2,p1); + { proc():= is never possible } + if token=_ASSIGNMENT then +begin + Message(parser_e_illegal_expression); + p1.free; +