Anonymous methods are generating a lot of buzz in the Delphi community right now, being one of the bigger changes heading our way in the forthcoming Tiburon (Delphi 2009), along with Unicode and Generics.
Unlike Unicode and Generics however, the usefulness of anonymous methods is not nearly as clear.
On Joel On Software
Joel On Software has been invoked as an authority. Unfortunately, as Joel himself has previously admitted, he does not actually use Delphi, and a great deal of the usefulness he sees in anonymous methods seems to stem from this fact, in that what he describes as the principal benefits of anonymous methods has always been achievable in Delphi.
To paraphrase his material on this point:
procedure Cook(const aIngredient1, aIngredient2: String; const aCookingMethod: TCookingMethod); begin Alert('get the ' + aIngredient); aCookingMethod(aIngredient1); aCookingMethod(aIngredient2); end; Cook( 'lobster', 'water', PutInPot ); Cook( 'chicken', 'coconut', BoomBoom );
Joel asks: Can your language do this?
If the language is Delphi, yes. We don’t need anonymous methods for this.
But that is not the only use for anonymous methods espoused by Joel. He goes on to talk briefly about threading, before seeming to me to go off at something of a tangent to talk about MapReduce, where the applicability of anonymous methods is a little hard to fathom (in the context of languages that “can do this”) and I’m certain that the actual Google MapReduce implementation is a great deal more complex than Joel’s summary, but I’m not so sure that anonymous methods were critical to it’s success, rather than just happening to be a part of it.
One wonders if they would have featured at all if Google had used Delphi. 🙂
Anonymous Methods and Threading
In a different blog, Lars Fosdal also mentions threading.
Coming at the subject with the Delphi perspective that Joel does not have, Lars aligns the benefits of anonymous methods against the failings of TThread. Not unreasonable, given that TThread is the “out of the box” approach to threading in Delphi and it undeniably has a number of shortcomings. But TThread is not the only game in town for Delphi developers. I myself have needed a more lightweight approach to threading in the past. So I made one. I called it TMotile (a work by Peter F Hamilton brought the term motile to my attention, and the fit was obvious in my mind).
My TMotile class can be used in two ways. First to execute some procedure in a fire-and-forget background thread:
procedure SomeProcedure; begin // ... end; TMotile.Execute( SomeProcedure );
Or secondly in a way perhaps more familiar to those already used to TThread:
TMyMotile = class( TMotile ) protected procedure Execute; override; end; procedure TMyMotile.Execute; begin // ... end; TMyMotile.Create;
With anonymous methods that fire-and-forget usage could be made simpler:
TMotile.Execute( procedure begin // ... end; );
Of course, that “simpler” observation is, I think, debatable. If it is “simpler” at all, then actually it isn’t by very much, and arguably it introduces an unacceptable level of noise into the code. This doesn’t appear to be the case when viewed in isolation, but in the context of a longer sequence of code where only one statement is invoked to create a thread to handle background execution of some other code, seeing the body of that background code is “noise”.
There is an even greater advantage when the body of the anonymous procedure captures state from the context in which it is created. In those situations then this usage simply cannot work without anonymous methods, requiring a subclass of TMotile (or TThread) in order to explicitly capture that state from the context and make it available to the threaded procedure:
TMyMotile = class(TMotile) private fContextVar: ...; protected procedure Execute; public property ContextVar: .. write fContextVar; end; procedure TMyMotile.Execute; begin .. fContextVar ..; end; mm := TMyMotile.Create; mm.ContextVar1 := SomeContextValue; mm.Execute;
Which can be replaced with:
mm := TMotile.Execute( procedure; begin .. SomeContextValue ..; end; );
Which is undeniably neat. Both in the sense that is it clever and a great deal more concise. But I am still not convinced that this convenience is worth getting all that excited about – It has not enabled anything that was previously impossible, nor even especially difficult either to implement or to understand, and that noise is still there.
Indeed, anonymous methods do not even eradicate the infrastructure that is required to support it, they just hide it.
If it’s hidden, I can’t fix it or improve it or adapt it to better suit a specific use. If it’s hidden I may never the less need to take it into account to understand what unfamiliar code does and how – hiding it just makes it that much harder to identify and requires me to bring that knowledge of the hidden details with me.
For one thing of course, if this threaded code should later evolve and require some mechanism to pass information back to the context that originated it – or some other context – having that threaded code represented by an identified and referencable object provides exactly the mechanism we need. Without any such visible device, yet more wizardry is needed to provide that mechanism without resorting to simply exposing and extending the pretty basic infrastructure.
With Great Anonymity Comes Great Responsibility
Having anonymous methods in the language does not compel us to use them, and unlike Generics or even Unicode to an extent, using them in your code will not compel others to use them either, if they do not wish to. So what’s the harm?
Well, of course, the same could be said of with – and indeed, the similarity is quite striking. Both provide a means of hiding an implementation detail and avoiding the onerous task of encumbering that detail with an identity. with is, somewhat famously, regarded with disdain for the way that this hiding of details introduces problems. Not least when debugging (and the prospect of debugging anonymous methods is similarly intriguing, to say the least).
So if we all only use anonymous methods responsibly, we’ll be fine.
Which call to mind Eddie Izzard’s response to the NRA’s assertion that “Guns don’t kill people, people kill people” …..
…and monkeys do too (if they have a gun)
Speaking of Eddie Izzard, if you haven’t heard his Death Star Canteen sketch, then allow YouTube to introduce you to in glorious LegoVision.
Any excuse. 🙂
No not a further examination of threading, but the use of one thing to draw a parallel, or a comparison, with something else. In this case, let us imagine we are putting together a web site, and on some of our pages we have some content that isn’t directly relevant to the page it is on. Content that certainly doesn’t have to be read at the time, but which perhaps isn’t likely ever to be read alone. It’s not going to appear in our site navigation or even the table of contents necessarily.
It’s content, but it doesn’t have an absolute identity outside of some other context in which it is referenced. On the printed page, it would be a footnote or an entry in the bibliography.
These things to a written language are, essentially, what anonymous methods are to our code.
So why do we have footnotes? Why do we use links in web pages, instead of just quoting, verbatim and inline, the content we would like the reader to perhaps come back to later, and just require them to skip over it in the meantime.
The question is of course rhetorical.
This is essentially what anonymous methods will bring to your source code.
I may never write code this way myself, but I am not looking forward to the first time I have to read someone else’s code written that way.
So, When Should They Be Used?
The title of this post posed a question, and it’s only fair that I answer it.
As things stand, I would have to say that anonymous methods should be used only when you absolutely have to, and so far I have not yet seen an example where anonymous methods are the only way to achieve anything.
I may yet change my mind, and more examples have been promised by CodeGear. But CodeGear didn’t invent these things, and they aren’t new, as plenty of people point out – compelling and obvious examples should already be readily available, albeit not in Delphi.
Where are they?