Jump to content
EugeneK

Anonymous methods as interfaces

Recommended Posts

In Dalija Prasnikar's smart pointer unit here https://github.com/dalijap/code-delphi-mm/blob/master/Part5/SmartPointer/uSmartPtr.pas#L8  following code is used

 

  ISmartPointer<T> = reference to function: T;

  TSmartPointer<T: class, constructor> = class(TInterfacedObject, ISmartPointer<T>)

basically anonymous method declaration is equivalent to interface.

Is it documented somewhere? I tried looking at Interfaces and Anonymous method sections in documentation but can't find anything.

  • Like 1

Share this post


Link to post
1 hour ago, EugeneK said:

basically anonymous method declaration is equivalent to interface.

That is correct. Basically, an anonymous method is implemented as an interfaced object with an Invoke() method that matches the signature of the anonymous method.

https://stackoverflow.com/questions/39955052/how-are-anonymous-methods-implemented-under-the-hood

1 hour ago, EugeneK said:

Is it documented somewhere?

Not by Embarcadero, as it is an implementation detail of the compiler.  But it is a well-known implementation that is documented on numerous sites/blogs.

  • Like 1

Share this post


Link to post
Quote

Not by Embarcadero, as it is an implementation detail of the compiler.  But it is a well-known implementation that is documented on numerous sites/blogs.

So it is not officially supported and theoretically can be changed?

Share this post


Link to post
1 hour ago, Dalija Prasnikar said:

There would be no reason to change it, short of removing anonymous methods.

I think anonymous methods will still work without ability to explicitly use them as interfaces. I could not find it being used in this way anywhere in Delphi RTL.

Share this post


Link to post
Just now, EugeneK said:

I think anonymous methods will still work without ability to explicitly use them as interfaces. I could not find it being used in this way anywhere in Delphi RTL.

You don't understand. They are implemented as interface based class. Without that implementation they wouldn't work at all. It is just that compiler hides that fact from us and automatically creates everything needed.

Share this post


Link to post
6 minutes ago, Dalija Prasnikar said:

You don't understand. They are implemented as interface based class. Without that implementation they wouldn't work at all. It is just that compiler hides that fact from us and automatically creates everything needed.

But this implementation does not have to be accessible as interface to end user for it to work.

Share this post


Link to post
2 hours ago, EugeneK said:

So it is not officially supported and theoretically can be changed?

In theory, pehaps.  In practice, not likely.

32 minutes ago, EugeneK said:

I think anonymous methods will still work without ability to explicitly use them as interfaces.

Using an interface makes for easier lifetime management.  An anonymous procedure can capture variables, so they have to be stored somewhere that can follow the procedure as it's passed around.  As long as someone has a reference to the procedure, the variables have to be maintained.  So it makes sense for the implementation to use a separate object for that storage.  And someone has to create that object, and more importantly destroy that object after everyone is done using it.  Since Delphi doesn't have garbage collection, using a reference-counted interface makes sense.

12 minutes ago, EugeneK said:

But this implementation does not have to be accessible as interface to end user for it to work.

How would you propose doing it without using an interface?  The only way that makes sense to me is a managed record with a manual reference count (but managed records didn't exist yet when anonymous procedures were introduced).

 

In any case, just because an anonymous procedure is implemented using an interface does not mean you have to use it as an interface.

Share this post


Link to post
On 3/15/2024 at 2:11 PM, Remy Lebeau said:

How would you propose doing it without using an interface?  The only way that makes sense to me is a managed record with a manual reference count (but managed records didn't exist yet when anonymous procedures were introduced).

I'm not saying doing it without interface, I mean hiding this implementation detail from the users. I.e. not allowing to manually implement this interface in code. This won't break any documented uses of anonymous methods.

Share this post


Link to post
13 hours ago, EugeneK said:

I'm not saying doing it without interface, I mean hiding this implementation detail from the users. I.e. not allowing to manually implement this interface in code. This won't break any documented uses of anonymous methods.

But that's the way it currently works! Of course you can design your own interface + implementor that acts exactly like anonymous methods act now, but that would still not be the same implementation the compiler creates behind the curtain now.

Share this post


Link to post
31 minutes ago, PeterBelow said:

But that's the way it currently works! Of course you can design your own interface + implementor that acts exactly like anonymous methods act now, but that would still not be the same implementation the compiler creates behind the curtain now.

I'm not sure you understand my point. What I'm saying is that Embarcadero can theoretically hide ability to implement this interface manually and all existing code that uses anonymous methods in documented way will still work. Or they can change interface, say, rename Invoke to InvokeEx for some reason.

So manually implementing this interface is a hack that can randomly stop working. Although I agree that probability is low that they will change it.

Share this post


Link to post
Posted (edited)

As someone using this "feature", I can tell you that the moment they break it I will pester them with issue reports until they revert this "fix".

 

There has been another hidden "feature" in the past: adding operator overloads to records via helper when they are named in a particular way - since a few Delphi versions you can do that natively by declaring operator overloads on record helpers without this "hack" - but the hack still works.

 

Since there is no official language specification and it's basically "if it compiles, it's valid code" I assume this to be added to the documentation rather than to be changed. Embarcadero has not changed things that are worse for backward compatibility reasons in the past so I assume they won't touch this either.

 

The more important thing they should address with anonymous methods is this: not allocating any heap memory when no capturing is happening. The code would look similar to that for the comparers for intrinsic types in System.Generics.Defaults.pas

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
59 minutes ago, EugeneK said:

What I'm saying is that Embarcadero can theoretically hide ability to implement this interface manually

You can't manually implement the interface of an anonymous method.  It is a compiler-generated type that you can't refer to. And it doesn't have a Guid assigned to it, so you can't query for it at runtime, either.  All you can do is hold a reference to it.  The compiler generates the necessary code to call its Invoke() when it sees user code trying to call the anonymous method.  It is possible to manually call Invoke() directly (by declaring the interface manually, and then casting the anonymous method), but there is no good reason to ever do that.

59 minutes ago, EugeneK said:

Or they can change interface, say, rename Invoke to InvokeEx for some reason.

They could, and that shouldn't change anything at all, because user code shouldn't ever be calling Invoke() directly to begin with.

59 minutes ago, EugeneK said:

So manually implementing this interface is a hack that can randomly stop working.

Nobody is "manually implementing" the interface of an anonymous method.

Share this post


Link to post
Posted (edited)
3 hours ago, Remy Lebeau said:

You can't manually implement the interface of an anonymous method.

Yes you can, the first post in this thread contains the code doing exactly that.

3 hours ago, Remy Lebeau said:

They could, and that shouldn't change anything at all, because user code shouldn't ever be calling Invoke() directly to begin with.

In fact, until some version a few years ago it was possible to call Invoke.

Still, if the method reference type has {$M+} enabled you can get RTTI for its Invoke method and dynamically invoke it. And there are more than one (Spring) libraries that do that.

 

Also please let's get the terminology right - this is something that hugely annoys the heck out of me - because when talking about this subject everything is being called anonymous method but is incorrect.

TProc = refererence to procedure;

This is a method reference type - yes, even the official documentation is a mishmash. There is nothing anonymous about this type - it has a name:  TProc.

procedure Foo;
begin
end;

var
  p: TProc;
begin
  p := Foo;
end.

Again there is no anonymous method in this code - the variable p of the method reference type TProc is being assigned. The variable has a name and thus also is not anonymous.

var
  p: TProc;
begin
  p := procedure begin end;
end.

Now we have an anonymous method - the code block assigned to p has no name. This is an anonymous method that is assigned to the method reference variable p.

 

Also see:

https://en.wikipedia.org/wiki/Closure_(computer_programming)#Anonymous_functions

https://en.wikipedia.org/wiki/Anonymous_function

Edited by Stefan Glienke
  • Like 3

Share this post


Link to post
2 hours ago, Stefan Glienke said:

Also please let's get the terminology right -.....


TProc = refererence to procedure;

This is a method reference type - yes, even the official documentation is a mishmash.

Yes, right on the point.

 

This is very confusing, as there is anonymous methods and there is anonymous procedure/function.

These are different things, methods are aware of self and it is a must to have, while the latter don't have self.

Share this post


Link to post

@EugeneK There is a short section in (at least) the Delphi 11 Alexandria version of Marco Cantu's Handbook (and probably earlier versions) called : Implementation of Anonymous Methods

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×