Understanding Anonymous Methods

Posted by on in Blogs
It appears that the next version of Delphi will support a feature called anonymous methods.  The circumstances in which one might use an anonymous method do not seem to be obvious to everyone in the Delphi community, so I'm going to attempt to answer that.

There is, at the moment, no shipping version of Delphi which includes anonymous methods, so I'm basing my discussion on my experiences using them in other environments.  The final, released version may include a different feature set and what I describe below.

First, let's answer the obvious question, with the obvious answer: What can I do with an anonymous method which I can't do without an anonymous method?  Nothing.  In fact, there's nothing which you can do with Delphi which you cannot do with ASM.  Anonymous methods are a convenience suited to certain styles of programming, like strong, static typing, and object orientation.  They will probably seem useless to people who do not adopt those styles of programming, just as virtual methods will seem useless or dangerous to a non-OO programmer.

I suspect that most "Pascal programmers" view procedures as being a fundamentally different type than other data structures.  In the static typing the world, we typically define the shape of a data structure at compile time, and the contents at runtime.  By contrast, even when passing a procedure as an argument to another procedure, in older versions of Delphi we have had to define both the shape and the contents of the procedure at compile time.  Only the arguments can be set at runtime.

With one exception: Nested procedures.  Let's talk about that, for a moment.

Nested procedures, scope, and variable capture

Nested procedures are procedures declared inside of another procedure.  This essentially reduces the scope of the nested procedure to being callable within the nesting procedure only: Here is a simple example.

procedure MyClass.Foo;
i: integer;
procedure Bar;
i := 1;

procedure MyClass.Other;
Bar; // compiler error

The output of calling Foo will be the following lines, written to the console:


There are two things to note here. First, note that Bar can "see" the variable i, which is declared as a member of Foo.  Outside of the Delphi community, this feature is often called variable capture, although for some reason that term doesn't seem to be common within the Delphi community.  But that's what it is.  Second, note that the scope of Bar is limited to the Foo procedure.  That's nice, from a modularity point of view, but it's absolutely critical when combined with variable capture.  Outside of the scope of Foo, the variable i has no context/value.

Even the inside of the Foo procedure, however, you cannot assign Bar to an event property or any other procedure reference.  The way that variable capture is handled internally makes this impossible, without some hacking.

Now, one might reasonably ask what the value of that variable capture feature is.  After all, in the example above, I could just as easily have passed i to Bar as a var argument, and the result would have been the same, albeit at the expense of a bit more typing.  Indeed, in the case of nested procs, variable capture is really little more than a labor saving device.

Anonymous methods

Returning to anonymous methods, let's start with the definition.  An anonymous method, as the phrase implies, is a method without a name.  In addition to being nameless, anonymous methods, in most environments, include some form of variable capture.

According to the Delphi roadmap, anonymous methods will be assignable to procedure references.  You can see an example of this on Andreano Lanusse's blog.

The combination of assignability and variable capture is quite powerful, because it effectively means that I can change the signature of the procedure reference type in the definition of the anonymous method.  That is, I can pass values which are not included in the arguments declared in the procedure reference declaration.  That's unnecessary and perhaps even confusing if I've written the "reference to procedure" declaration myself.  In this case, I should just include the necessary arguments.  If, however, the declaration was written by someone else — say, the author of a framework — it's incredibly convenient, as I can now pass values which the framework author hasn't included on my behalf.

In the past, I've had to use some fairly ugly kludges to get around this limitation, such as declaring a new class field to hold some value which I only wanted to use inside of an event handler whose signature I did not control.  With variable capture, these kludges are unnecessary.

This returns us to the issue of "programming worldview" to which I alluded at the beginning of this post.  To a programmer unaccustomed to thinking of functions as values, with functions living in one little container, and data structures living in another little container, this may all seem a bit odd.  But if you consider a function as a bit of data, defining both behaviors and passed values, it's not so strange.  Of course we can define data at runtime, just like we've always done.
Tags: Delphi


  • Guest
    Erik Monday, 04 August 2008

    I think that some of the confusion is caused by a literal interpretation of the name of this feature. The critical benefits of "Anonymous Methods" have less to do with them not being named. If you were able to assign names to them (which is similar to what you do when when you assign them to a variable named with a given identifier), their critical features would still be in place. You mainly limit the calling scope to those that have access to the declaration location or any variable that holds a reference to the anon method, and provide the features such as variable capture. I wonder if the Delphi implementation is automatically thread-safe with respect to writing to the local variables in multiple threads, etc., or if some language features would be made available to easily control access to those variables...

  • Guest
    El Cy Monday, 04 August 2008

    An interesting read in this context ....

    The Evolution Of LINQ And Its Impact On The Design Of C#

  • Guest
    Lars Fosdal Tuesday, 05 August 2008

    I suspect that one of the reasons for so many having problems with realizing the impact of anon.methods, is that all the examples so far have been trivial code that can be done without anon.methods.

    As soon as we start using generics, the benefits are much more clear (TCollection.ForEach f.x.), but even those can be done using sub-classing and without anon.methods.

    The discussion then appear to derail over in which style (inline declared anon methods, or object protected virtual methods) that is preferable.

    I made some speculations on when they could be used

    but we could use something more elaborate and thought through which really shows off the power of anon.methods.

    P.S. Why did you add IntToStr(i) inside the WriteLn? :)

  • Please login first in order for you to submit comments
  • Page :
  • 1