Post a Comment
" ... anonymous methods may just be an example of some inventive person at Microsoft being a bit too clever."
Ditto to the previous "Lambda" comment. Anyone care to take a stab at how many languages already implement anonymous functions/methods?
Besides, if Microsoft invented them, they wouldn't be "anonymous" -- they'd be named either "MS method" or "Visual function". ;-)
RE: the serverside article, a very good read.
Re anonymous functions and capturing local variables...
Wow that seems like an easy to misunderstand 'feature'. It seems to me that the rules are not well defined on how local variables are captured in to anonymous delegates. Is there a reference? Why do they get promoted to some special semi-real variables instead of something 'simple' that won't get abused. I'm a java person so I'm biased, but I find that the enforcement that local variables cannot migrate to anonymous inner class definitions without being declared final is a good one since it makes debuggin easier, easier to read code and makes it 'more' deterministic rather then his own example where you're not sure what will happen due to thread inconsistincy.
The rules seem well-defined enough to me. I think the article's focus on the generated code somewhat confuses the underlying simplicity of the mechanism. It helps to think of closures as just being objects, with a single method created at runtime. When a function may create a closure, it's stack frame is allocated on the heap. Any created closures retain a pointer to that stack frame --- it is this stack frame that comprises the "member data" of the closure. Therefore, two closures created by the same function on different invocations will refer to two different stack frames, while two closures created by the same function on a single invocation will refer to the same stack frame.
Do you really think that this description simplifies the matter at hand, since you use terms like 'stack frame,' 'pointer,' 'heap' and so forth?
These are essentially lexical closures and could be understood abstractly in terms of capturing the enclosing lexical environment, which means that free variables in a method body will be substituted from the calling environment at runtime. That's it. It's not especially complicated. All they have to do is think of a lexical closure capturing free variables from the environment similarly as they might think of free variables (fields, for example) in a method body being obtained from the implicit receiver at method invocation.
class X
int z
public int Foo(int y) { return y + z }
public X(int s, int r) { z = s * r }
e1 = X(1, 2)
e1.Foo(1)
e2 = X(2, 3)
e2.Foo(1)
Here z is substituted with e1.z and e2.z respetively in the method when Foo is invoked.
public delegate int FooFN(int y)
public FooFN MkFoo(int s, int r) {
return delegate(int y) { return s * r + y }
}
FooFN Foo1 = MkFoo(1, 2)
FooFN Foo2 = MkFoo(2, 3)
Foo1(1)
Foo2(1)
Here s and r are substituted by 1 and 2 in Foo1 and 2 and 3 in Foo2. This isn't all that hard to understand, and isn't bogged down by any implementation details like what sort of activation records one has.
I'm a java person so I'm biased, but I find that the enforcement that local variables cannot migrate to anonymous inner class definitions without being declared final is a good one since it makes debuggin easier, easier to read code and makes it 'more' deterministic rather then his own example where you're not sure what will happen due to thread inconsistincy.
But then you lose all the power of closures. The ability or the anonymous method to not only access, but mutate the local variables is very important. I use closures *all the time* in C# now that it has it and it has greatly increased code reuse (how can passing around closures/bits-of-code-with-context not?).
Thread safety is no more an issue with closures than with anything else you write. Java only avoids thread safety issues for some uses of closures by simply NOT supportig closures and not allowing you to write your code in that simple manner. If you try to emulate closures with Java and pass the closure to multiple threads, you will get into the exact same problems.
I recall Guy Steele saying that inner classes in Java originally permitted mutability, and that they scrapped them because people complained about the automatic heap allocation necessary for implementation.
It's not a matter of "non-determinism." Accessing a field without synchronization from multiple threads is fundamentally no different than accessing a captured variable from a closure with multiple threads without synchronization.
> Anyone care to take a stab at how many languages
> already implement anonymous functions/methods?
That's hardly possible because there is great number of languages that almost nobody knows. I know about lisp/scheme, haskell, miranda, (s)(oca)ml, javascript(!), smalltalk, ruby, python, c#, Java has very lame read-only closures a.k.a anonymous inner classes, and probably future versions of VB.NET 
I wish he had said this particular statement Linguistically, I love methods, but as a practical matter anonymous methods may just be an example of some inventive person at Microsoft being a bit too clever. at the beginning, so I would have known to stop right there, and would have been spared having to read the whole article.
Why is it that you think these people don't have degrees in CS? Undergraduate CS curriculums can be fairly superficial in their treatment of everything, including programming languages. Then there are the many deficiencies of the actual students...
However, in the case of Paul Kimmel has:
Michigan State University East Lansing, MI
Computational Mathematics
That's a little vague in terms of what level of education was obtained, but from a casual perusal of http://www.mth.msu.edu/UGAdvise/computational_math_bs_new.html I can get a rough idea of what he'd have studied were I to ignore that he doesn't mention the years he attended (and thus there may be some variations in curriculum requirements).
His resume also appears to state that he has written or co-written about sixteen books. Of course they all look like the sort of books one would be better of burning for heat than reading. Perhaps even better for throwing at people.
(with horrible syntax)
Guy Steele:
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg04044...
Can you explain further what's achieved through that [the use of a final object[]]? It still won't capture local variables, right?
It can capture local variables. Consider:
String s = "Hello";
final object[] closure = new object[]{s};
return new Runnable () {
public void run () {closure[0] = "modified";}
};
There you have it: s is captured within closure, because final applies to the object reference closure, not to the contents of closure.
Why would you want to do this? Because full capturing closures are terribly useful in some circumstances, where the equivalent is to manually do what C#'s anonymous delegate mechanism does -- create a new inner class and populate its variables. Creating a new class requires more lines of code, which requires reading (and keeping in human memory) more information to understand what's going on.
The problem is that you want to create an anonymous class that returns something to the local context. Since anonymous classes capture only final local variables, you just have a local final object[] with a length of 1 and store the result in the first position of the array.
It is just an ugly hack. But I saw it quite a lot back when I wrote java code (I have switched to .NET in 2003).
Since anonymous classes capture only final local variables, you just have a local final object[] with a length of 1 and store the result in the first position of the array.
I see. That really is ugly.
I guess Sun could employ the same trick, or an automatically generated holder object, to implement proper closures in the language without having to change the JVM.
Another problem with Java "closures" is that you can't throw exceptions out of its body, unless you declared it in the interface. You can't throw them in Runnable because Runnable declares to throw no exceptions, and if you declare your own Runnable-like interface, you have to declare all possible exceptions there. And you have to repeat these declarations in *each* anonymous inner class you write.




