CSC/ECE 517 Fall 2011/ch1 1c sj: Difference between revisions
No edit summary |
(Formatting edits and added section on anonymous functions and the difference between them and closures.) |
||
Line 1: | Line 1: | ||
==Closure vs. Methods (and why anonymous functions are NOT closures)== | |||
A closure can be defined as a First-Class function that retains the variables and values it references, even when the original variables and values go out of scope. Depending on the implementation and the language, a closure will either have the values passed in by value (ie. each closure will have it’s own individual values) or by reference (ie. each closure will share a reference to ONE value). Differences between languages will be discussed in the second portion of this article. | A closure can be defined as a First-Class function that retains the variables and values it references, even when the original variables and values go out of scope. Depending on the implementation and the language, a closure will either have the values passed in by value (ie. each closure will have it’s own individual values) or by reference (ie. each closure will share a reference to ONE value). Differences between languages will be discussed in the second portion of this article. | ||
A method is a function associated with a class. Methods have access to data stored in the class instance they are associated with and are able to act on that instance of the class. Methods can be bound to a class at compile time, which is a static binding, or at run time, which is a dynamic binding. | A method is a function associated with a class. Methods have access to data stored in the class instance they are associated with and are able to act on that instance of the class. Methods can be bound to a class at compile time, which is a static binding, or at run time, which is a dynamic binding. | ||
===Examples of Closures vs. Methods=== | |||
In order to differentiate between the two, here are two examples that produce the same result: | In order to differentiate between the two, here are two examples that produce the same result: | ||
Example 1: Closure (Ruby example) | Example 1: Closure (Ruby example) | ||
times = ['a', 'b', 'c'].collect {|item| item.upcase} | times = ['a', 'b', 'c'].collect {|item| item.upcase} | ||
Line 19: | Line 18: | ||
Example 2: Method (C++ style) | Example 2: Method (C++ style) | ||
class someObject() { | class someObject() { | ||
Line 46: | Line 44: | ||
Example 3: Closure (Ruby) -- Test if an employee is a manager | Example 3: Closure (Ruby) -- Test if an employee is a manager | ||
def managers(emps) | def managers(emps) | ||
Line 54: | Line 51: | ||
Example 4: Method (C++ style) | Example 4: Method (C++ style) | ||
public static IList Managers(IList emps) { | public static IList Managers(IList emps) { | ||
Line 64: | Line 60: | ||
In this example, three lines of Ruby code (closure construct) replicates the method that would need to be embeded in an object that held a list of all employees. The savings in code writing extends far beyond just the size of these two methods. | In this example, three lines of Ruby code (closure construct) replicates the method that would need to be embeded in an object that held a list of all employees. The savings in code writing extends far beyond just the size of these two methods. | ||
====A note on Anonymous Functions==== | |||
Consider the following function: | |||
function helloWorld() { | |||
return function() { | |||
alert("Hello World"); | |||
} | |||
} | |||
'''The returned function in this case is anonymous but not a closure.''' | |||
Now look at the following function: | |||
function helloWorld() { | |||
var a = "Hello World"; | |||
return function() { | |||
alert(a); | |||
} | |||
} | |||
In this case, the returned function is a closure '''because it refers to helloWorlds's local variable a and causes the lifetime of a to extend beyond the normal scope of a, which is the helloWorld() function.''' The returned function "closes over" the variables in helloWorld() and have access to that local environment, even after the outer function goes out of scope. | |||
Revision as of 19:06, 4 September 2011
Closure vs. Methods (and why anonymous functions are NOT closures)
A closure can be defined as a First-Class function that retains the variables and values it references, even when the original variables and values go out of scope. Depending on the implementation and the language, a closure will either have the values passed in by value (ie. each closure will have it’s own individual values) or by reference (ie. each closure will share a reference to ONE value). Differences between languages will be discussed in the second portion of this article.
A method is a function associated with a class. Methods have access to data stored in the class instance they are associated with and are able to act on that instance of the class. Methods can be bound to a class at compile time, which is a static binding, or at run time, which is a dynamic binding.
Examples of Closures vs. Methods
In order to differentiate between the two, here are two examples that produce the same result:
Example 1: Closure (Ruby example)
times = ['a', 'b', 'c'].collect {|item| item.upcase} puts times.join(", ") + " easy as 1, 2, 3."
Result → A, B, C easy as 1, 2, 3.
Example 2: Method (C++ style)
class someObject() { void easyAs(String[] values) { String final = ""; for (int i = 0; i < values.size; i++) { final += values[i].toupper(); if ( i < values.size - 1) final += “, “; } final += “ easy as 1, 2, 3.”; cout << final; } } someObject obj = new someObject(); String[] s = {“a”, “b”, “c”}; obj.easyAs(a);
Result → A, B, C easy as 1, 2, 3.
As you can see from the above examples, the closure method is much more concise and easier to read. Both pieces of code take an array of strings, process them, and prints out a single string → “A, B, C easy as 1, 2, 3.”
At first glance this may look like syntactic sugar, but consider the following example:
Example 3: Closure (Ruby) -- Test if an employee is a manager
def managers(emps) return emps.select {|e| e.isManager} end
Example 4: Method (C++ style)
public static IList Managers(IList emps) { IList result = new ArrayList(); foreach(Employee e in emps) if (e.IsManager) result.Add(e); return result; }
In this example, three lines of Ruby code (closure construct) replicates the method that would need to be embeded in an object that held a list of all employees. The savings in code writing extends far beyond just the size of these two methods.
A note on Anonymous Functions
Consider the following function:
function helloWorld() { return function() { alert("Hello World"); } }
The returned function in this case is anonymous but not a closure.
Now look at the following function:
function helloWorld() { var a = "Hello World"; return function() { alert(a); } }
In this case, the returned function is a closure because it refers to helloWorlds's local variable a and causes the lifetime of a to extend beyond the normal scope of a, which is the helloWorld() function. The returned function "closes over" the variables in helloWorld() and have access to that local environment, even after the outer function goes out of scope.
Mimicking closures in other languages:
JavaScript has native support of closures. In fact, closures can be set up in JavaScript without even realizing that they’ve happened:
<form name="buttonForm"> </form> <script> function createButtons() { var i; var button; for (i = 1; i <= 5; i += 1) { button = document.createElement("input"); button.type = "button"; button.id = "b" + i; button.value = "button " + i; button.onclick = function() { alert("button " + i); } document.forms.buttonForm.appendChild(button); } } createButtons(); </script>
In this example, a closure is created when the onclick attribute of each button is set to an anonymous function that references the variable i in the createButtons function. The intention here is that clicking any of the buttons will result in an alert that display “button x” where x is the number of the button. Unfortunately, this actually results in “button 6” being displayed for each button.
The reason for this is that i in the anonymous function is a reference to the I declared in createButtons, so clicking any of the buttons gives us the latest value of i rather than the value of i at the time the anonymous function was created. To get around this requires a more sophisticated function:
button.onclick = (function (e) { return function() { alert("button " + e); } })(i);
Now the alert no longer references i directly, but instead references a local parameter that has been set to the value of i at the time the function was created. The outer function is evoked immediately, but returns a function that has a closure on the local parameter. Unlike i, this value won’t change.
It’s been said that closures can be approximated in Java using anonymous inner classes. If that’s the case, then we should be able to mimic both of the behaviors here, that is, we should be able to create buttons that refer to either the latest value of a variable like example 1, or have each button refer to the value of that variable at creation time like example 2. The first attempt at a createButtons function in Java similar to the one in the JavaScript examples might look like this:
public void createButtons() { final int WIDTH = 50; for (int i = 1; i <= 5; i++) { JButton button = new JButton("Button " + i); button.setBounds(WIDTH * 2 * (i - 1) + WIDTH, 50, WIDTH * 2, 25); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { // This next statement results in a compile time error. JOptionPane.showMessageDialog(panel, "Button " + i, "Button Info", JOptionPane.INFORMATION_MESSAGE); } }); panel.add(button); } }
Unfortunately, as indicated by the comment, this results in a compile time error:
local variable i is accessed from within inner class; needs to be declared final
The problem with this, however, is that i cannot be final because its value need to change in order for the for loop to work correctly. There are two workarounds:
1. Make i an instance variable:
private int i; ... public void createButtons() { final int WIDTH = 50; for (i = 1; i <= 5; i++) {
This does mimic the behavior of the first example, but in a very dissatisfying way. Before i was only available within the scope of the for loop, but now it’s available to the entire object. This is rather disconcerting because it means that element of the object other than the buttons can access i, potentially changing its value.
Also, having an anonymous inner class isn’t even necessary for this work; a regular inner class would be just as effective:
button.addActionListener(new ButtonListener()); ... private class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { // This still works just fine. JOptionPane.showMessageDialog(panel, "Button " + i, "Button Info", JOptionPane.INFORMATION_MESSAGE); } }
This clearly indicates that we no longer have any type of closure at all, but are simply using standard OOP. A slightly better option might be to subclass JButton and create a private static instance variable that can be set to the value of i, but we can’t allow createButtons to set this variable without letting any method in the object to set the variable.
From this we see that the type of closure demonstrated in the first example cannot be effectively mimicked in Java without giving the rest of the object access to the variables in the closure as well.
2. Set some other variable equal to i and make that variable final:
... // Need this for closure to work final int j = i; button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { // Now this works fine. JOptionPane.showMessageDialog(panel, "Button " + j, "Button Info", JOptionPane.INFORMATION_MESSAGE); } });
It turns out that this is a good match to the second JavaScript example. Each button has access to the value of i at the time the closure was created. Since this was also the desired behavior, then it appears using an anonymous class can be used to effectively mimic closures in at least some circumstances.
Note, however, that setting up a closure this way in JavaScript was actually a bit more convoluted. It seems likely that the type of closure given in the first example is actually much more common, and this is the type of closure that Java has difficulty in handling. This means that the closure pattern will not be available for many cases where it might be useful in Java, and likely in other languages as well.
RESOURCES
http://en.wikipedia.org/wiki/Closure_(computer_science) http://hendryluk.wordpress.com/2008/05/17/anonymous-method-is-not-closure/ http://www.javascriptkit.com/javatutors/closures.shtml http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html http://hendryluk.wordpress.com/2008/05/17/anonymous-method-is-not-closure/ http://stackoverflow.com/questions/111102/how-do-javascript-closures-work http://pawelzubkiewicz.blogspot.com/2009/06/poors-man-closures-in-java.html http://ivan.truemesh.com/archives/000392.html http://www.martinfowler.com/bliki/Closure.html http://nathansjslessons.appspot.com/lesson?id=1050&lang=en http://zadasnotes.blogspot.com/2010/10/leaky-ie-javascript-closures.html http://sleeplessgeek.blogspot.com/2009/12/so-what-are-these-closure-thingys.html http://stackoverflow.com/questions/111102/how-do-javascript-closures-work#112265 http://www.ibm.com/developerworks/java/library/j-cb01097/index.html http://innig.net/software/ruby/closures-in-ruby.rb http://www.oreillynet.com/onjava/blog/2006/08/will_we_have_closures_in_java.html http://blog.morrisjohns.com/javascript_closures_for_dummies.html http://groovy.codehaus.org/Closures http://rickyclarkson.blogspot.com/2007/10/why-java-needs-closures-it-already-has.html http://www.zetcode.com/tutorials/javaswingtutorial/swingevents/ http://www.martinfowler.com/bliki/Closure.html Douglas Crockford. JavaScript: The Good Parts