raganwald
(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Monday, November 14, 2005
  Repost: Closures in Ruby


(I wrote the original version of this page in 2002. I've made a few minor edits and added a comparison with Java's anonymous inner classes)

I briefly worked with a team that used Perl to implement high availability web applications. When discussing the language with the team’s technical lead, I pointed out that I was impressed with the fact that Perl implemented closures. Having written a Scheme interpreter, I considered closures a fundamental component of modelling procedures.

This led to a discussion of what was a closure, and what was it good for?

A closure encapsulates the execution of a one or more operations for side effects and/or the return of a value in the environment of the function’s definition where the closure was created.
From this definition, all functions, procedures, and methods in languages such as Java and Visual Basic are closures. When a programmer refers to a language as implementing closures, (s)he is really saying that the language permits the creation of arbitrary closures at run time. Scheme aficionados would say that languages like Perl, Lisp, and Ruby support first class closures: closures can be arbitrarily created and assigned as values to variables or returned from functions.

Since contemporary programming languages are lexically scoped, the environment of the function refers to the variables in scope at the time the function is defined. This includes temporary variables, variables that are normally created on some sort of stack and discarded when they “go out of scope.” When a closure is created, variables in scope must be preserved until the closure itself ceases to exist.

Here’s a Ruby closure demonstrating the fact that it ‘captures’ a variable in the scope of its definition:

def makeCounter
var = 0
lambda do
var +=1
end
end

c1 = makeCounter
c1.call
c1.call
c1.call

c2 = makeCounter

puts "c1 = #{c1.call}, c2 = #{c2.call}"

The two important things from this example are:

  1. Although var is no longer in scope once makeCounter returns, Ruby saves it for use in the closure.

  2. Each invocation of makeCounter creates a different var. The two counters do not interfere with each other.

What can you do with closures? Here’s something a bit more useful, a call-by-need thunk factory:

def delay(&procToDelay)
value = nil
return lambda do
if value.nil?
value = procToDelay.call()
else
value
end
end
end

def force(thunk)
thunk.call()
end

foo = delay do
puts "thinking about foo"
"fu"
end

bar = delay do
puts "thinking about bar"
"british american racing"
end

puts force foo
puts force bar
puts force foo
puts force bar

In this example, you have a simple facility for memoizing closures: they can be called repeatedly, but they only evaluate their operations once (provided the retun value is not nil). Obviously, this should not be combined with the previous example: call-by-need thunks are useful when there are no side effects of their evaluation.

Why Java's Anonymous Inner Classes do not implement closures

At first glance, an anonymous inner class in Java looks like it captures an environment. It has access to its enclosing instance's members. That looks an awful lot like the way a closure captures its environment.

But an anonymous inner class cannot access method variables or parameters. This is a crippling limitation. Consider:

interface Transformer {
int transform (int what);
}

class TransformerConstructionKit {

public static Transformer makeMultiplier (int timesWhat) {
return new Transformer () {
public int transform (int what) {
return what * timesWhat;
}
};
}

}

This is illegal in Java for some reason. Ok, I know what the reason is. But I don't have to like it, do I?

Labels: ,

 

Comments on “Repost: Closures in Ruby:
I was reading up on closures and it was mentioned that the C# anonymous methods is the .NET version of a closure.

Here is an example pf the code (indenting is screwed up):
==============================
class SomeClass
{
delegate void SomeDelegate();
public void InvokeMethod()
{
SomeDelegate del = delegate()
{
MessageBox.Show("Hello");
};
del();
}
}
==============================

Seems to me that you really have to put in a little work to get a "closure" from this where as the code you wrote in Ruby gives it to you for free.

Does the C# implementation still fit the need?
 
My understanding is that C# anonymous methods and iterators both act like closures.

The idea of an "anonymous method" has been around for a very long time and in a working computer programming language since 1959.

Alonzo Church described anonymous functions in his work on the "lambda calculus." This is the historical reason for Ruby's lambda keyword.
 
import java.lang.reflect.*;

interface Args {
Object length (Object parms);
}

class Closure {
private Object inst;
private Object[] parms;

public Closure ( Object inst, Object... parms)
throws Exception
{
this.inst = inst;
this.parms = parms;
}

public Object call( Method m, Object... parms)
throws Exception
{
return m.invoke(inst,(Object)parms);
}

public Object call( String m, Object... parms)
throws Exception
{
return call(inst.getClass().getMethod(m,Object.class),parms);
}
}

public class TestClosure {

public static void main(String[] args)
throws Exception
{

Closure c = new Closure(new Args () {
public Object length (Object parms) {
System.out.println(parms);
return parms;
// still need to pass clones of the parmaeters rather than references
}}, (Object) args
);
System.out.println(c.call("length"));
}
}
 
In the Java example, declare the parameter as final. Then it works just fine.

interface Transformer {
int transform(int what);
}

class TransformerConstructionKit {
public static Transformer makeMultiplier(final int timesWhat) {
return new Transformer() {
public int transform(int what) {
return what * timesWhat;
}
};
}

public static void main(String[] args) {
Transformer t = TransformerConstructionKit.makeMultiplier(2);
Transformer u = TransformerConstructionKit.makeMultiplier(5);
System.out.println(t.transform(10));
System.out.println(u.transform(10));
}
}
 
Shaurz:

With respect... No :-)

I agree that in this example it will appear to produce the same result. If you like, please email and I will post another example where "final" will not work.

What is actually happening is this: Java will let you make something that looks like a closure, provided that it can optimize the result so that it is not a closure.

Allowing final variables from the enclosing scope is free because Java actually optimizes this into a copy operation.

p.s. It seems unfair for me to say "I'll change my example if you can find a way to make it work in Java," but remember that we can always find a way to make any Turing complete language produce the same result. What's under discussion is the way in which we express the mechanism for producing the result. making the paramter final actually says something quite different.
 
You may find this article on recursion interesting:

http://www-128.ibm.com/developerworks/linux/library/l-recurs.html?ca=dgr-lnxw06Recursion

There's a very good discussion of how to use closures to simplify recursive functions.
 
Very quickly, the reason why allowing final variables is not the same thing as creating a closure is this:

Allowing a variable in scope means allowing both read and write access to the variable. Thus, the inner function or block must be able to update the variable.

In Java, you must use idioms like the one element array or an object with a getter/setter to force the variable onto the heap. In effect, ou're creating a Scheme-to-Java compiler where you are doing the translation by hand.

You aren't using a closure. This is painfully evident when you want to use closures regularly. You must constantly think about each variable: "will I need this in an enclosed scope? If so, declare it like this and access it like this. If not, declare it like that and access it like that."
 
Another discussion on closures:

http://blog.moertel.com/articles/2005/08/30/closures-and-the-professional-programmer
 
And here's an excellent CS explanation of what's going on when you use Java's final keyword to create a closed form:

http://tinyurl.com/8p8jf

Have a look at his explanation of using substitution to close an open form.
 




<< Home
Reg Braithwaite


Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

Books
What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

Share
rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /