Monday, July 28, 2008

Anonymous method details

There's an item on Reddit that caught my attention - it has a great title, Pascal gets closures before java - why hasn't the world ended?

I've contributed some replies and clarifications to the thread, so if you're interested in some of the details of one of the upcoming Delphi 2009's new features, pop over and take a glance.

I am going to write some more detailed posts here, focusing on use cases for anonymous methods in particular. Until I find the time to do that, the Reddit comments will have to do.

16 comments:

talios said...

Thanks for the clarifications and comments to both my post, and the reddit thread. Interesting reading. I look forward to learning more about it as well.

Jolyon Smith said...

What would be REALLY useful is some PRACTICAL examples of how this new feature gives a tangible benefit other than just the ability to tick another box in a "Me too" list of language features.

To be clear I see "it enables things like LINQ" to be a null response, since this is just a technology enabler, not a direct benefit to application developers.

i.e. to establish that a LINQ enabler is a benefit you first have to establish that LINQ itself provides some concrete benefit and isn't itself just obfuscation and syntactic sugar.


All of the examples so far quoted as "made possible" by closures/anonymous methods have been achievable with arguably less noise and greater clarity in Pascal for years already.

Barry Kelly said...

Don't worry, Jolyon, I'll present some things that I hope will tweak your interest. Patience...

Jolyon Smith said...

I genuinely look forward to it Barry.

All of the really useful things I have learned in life haven't needed explaining. I've seen it, understood how it works and immediately seen how to make it work for me.

That just isn't happening for me with these new language "advances".

I could just be getting old (still some way off 40 yet though, so I hope not), but then again it could just be that the need for change for changes sake has finally outstripped any real need for change.

I hope you can show me how this stuff is useful, if only so that I can pretend I saw it all along and was just waiting for everyone else to catch up.

;)

El Cy said...

Why this construct is named anonymous "method" … since all the demos so far shows anonymous "functions" or "procedures" …. A method related to an object instance … so there is also a real anomymous "method" construct like the one below supported ?

type
// object method reference
TObjectProc = reference to procedure(x: Integer) of object;

How about a a "class" anoymous method … see bellow ?

type
// class method reference
TClassProc = reference to class procedure(x: Integer);

Just wondering ....

Anonymous said...

How about using anonymous methods as "inline listeners" ?

Ex:

var
anonListener: reference to procedure(Sender: TObject) of object;
….

anonListener := procedure(Sender: TObject) begin ShowMessage(’Handled by an anmeth !’); end;
button1.OnClick := anonListener;

Question 1:
It will be the above syntax allowed ?

Question 2:
How about a more "inlined" (see closure support in other modern languages) variation bellow ?

button1.OnClick := procedure(Sender: TObject) begin ShowMessage(’Handled by an anmeth !’); end;

Question 3:

There is any support for the similar feature (closure) in C++ Builder ?

m. Th. said...

1. First of all, your product manager said in his blog that your picture needs updating. :-)

2. Given the following case:

TProc = reference to Procedure(x: TDateTime);

procedure Call(const aProc: TProc);
begin
aProc(Now);
end;

var
myProc: TProc;

begin
myProc := procedure(t: TDateTime);
begin
WriteLn(FormatDateTime(t));
end;

Call(myProc);

ReadLn;
end;

What time will be shown on the screen?

Anonymous said...

Thank you for working on real language enhancements to Delphi! Also, please tell about them and give as more ideas and real-world examples as you can, because it is actually sells upcoming version of Delphi. As you may remember how linq and closures were discussed for C# and you know how it attracts new users.

It would be very interesting to hear about multithreading and closures and about new generics collections and closures, especially is there something like:

List: TList{TMyClass} ; // {} used for angle brackets here

BinarySearch(List,
function(X: TMyClass)
begin
Result := X.MyField = Edit1.Text;
end;
);

is it possible to use Edit1.Text?
is there something like generic BinarySearch function to search any type of a list or collection?

Do you plan anything like STL for Pascal? :)

Barry Kelly said...

El Cy - it's just a name; don't read too much into it. In the end, however, the thing does actually turn into a method, on a class you can't see.

"anonListener" - (1) existing events are all method pointers, however a new type kind, method references, are needed to refer to anonymous methods because of state capture. Method references are reference counted, while method pointers aren't. Basically, without either GC or breaking lots of user code, we can't change events to be method references and be assigned anonymous procedures. So in short, no. (2) If OnClick had been a method reference, this would have been fine. (3) There is a basic level of interop support, but until lambdas get into C++ there won't be first-class level support.

m.Th - depends on the current time at the time of the call to Call / it's call to aProc.

Edit.Text guy: yes, it's possible to use Edit.Text, but be aware that a binary compare will want something like function(const left, right: string): Integer, rather than a boolean, because it needs an ordering relation. STL? There are new generic collections coming in Tiburon, but not at the depth or breadth of the STL. One step at a time!

Edit.Text said...

Sorry about mistakes in previous post, of course it needs to be a comparator, not just equality check.

As to the Edit.Text - how it will be handled internally? As I understand you, when you use local variable X, then it is actually moved into the compiler-generated class, but if I reference Edit.Text, would you move Edit somewhere? And if not, how it all would be handled?

Btw, any kind of Yield (from C# 2.0) in Delphi 2009? :)

Barry Kelly said...

@Edit.Text: Referencing 'Edit.Text' means capturing the implicit 'Self' - Self.Edit.Text. 'Self' is a hidden parameter.

When parameters get captured, rather than deleting the original (which would make no sense), instead the parameter's value is copied into the activation record's field of the same type, and all subsequent references to the parameter are replaced with a reference to the field. The end semantics are the same, since you cannot access the original parameter, so changes to the parameter value are still shared etc.

Re 'yield', aka iterators, not for Delphi 2009, no - but much of the groundwork has been done in the compiler, and they should be easily addable at some point in the future.

El Cy said...

10x for the detailed response so far... Just looking (again) for some straight answers [YES/NO] for the already mentioned (possible) closure syntax:

Q1 - closure for object methods:
"reference to procedure() of object" ?!

Q2 - closure for class methods:
"reference to class procedure() of object" ?!

Q3 - there will be any specific RTTI additions for closures ? S can we query the arguments/return type for the closure (somehow similar to methods)

Since for events you already explained that there will be no support for directly assigning a closure ... how about for the following (possible?) use cases (just let me know if these are allowed):

Q4 - usage as parameters :

type
TMyClosure: reference to procedure(const text: String);


procedure procWithClosure(closure: TMyClosure);

Q5 - "inline" declaration :

begin
...
procWithClosure(
procedure(const text: String) begin
writeln(text);
end;);
...
end;

Q6 - since you discussed about a new type (method reference) there is any way (RTTI ?) to use this support outside the closure construct. I mean for regular methods in already defined objects, can we get a method reference by a method name+parameters type (similar to what how Java 7 will introduce the closure+method handles - see Neal Gafter + John Rose)

Q7 - There is any support for currying into the new closure construct ?


10x in advance for any further details ....

Barry Kelly said...

@El Cy:

Q1, Q2 - 'method references', the new unified method reference syntax, can refer to (1) anonymous methods, (2) global routines, (3) instance methods, (4) class methods, (5) pointers to all of the above. The only thing it can't refer to is local routines, and that should change post-Delphi 2009.

Q3 - RTTI - method references are interfaces undernead the hood. If METHODINFO is on, then the Invoke method will get RTTI; I might consider forcing this on for all method references.

Q4 - method references as parameter types - that's fine, they'd be pretty pointless otherwise.

Q5 - "inline" anonymous method construction - similarly, this works, little point otherwise.

Q6 - other uses of method references - method references are just interfaces. Technically, you can write a class that implements a method reference.

Q7 - currying - nothing built in, but with some suitably written generic method reference types, you can write your own currying routine. It won't be doing partial application underneath the hood like a functional language though, so you won't get other benefits like extra optimization that a functional compiler could do.

El Cy said...

Just great .... 10x allot for the fast & detailed reply !

Of course that now we wait for the blowing-mind examples :)

Lars Fosdal said...

What are the variable scope rules for anonymous methods? I.e. what variables that are in scope where you define the anon.method can you actually use within it?

Anonymous said...

Procedural type is pointer to code.
Method type is also pointer to code, but provides additional context - object instance.
Anonymous method is another method type with a different context, something like local nested function type, on a wish list for about a decade:
http://cc.codegear.com/Item.aspx?id=16841