CSC/ECE 517 Fall 2011/ch1 1c ka
Wiki Chapter: CSC/ECE 517 Fall 2011/ch1 1c ka
Introduction
This article discusses the advantages of using closures over methods. There is a common argument over the necessity of closures and the way in which the closure behavior can be achieved by using anonymous classes. On the contrary, closures have more to their credit than what is the general perception that they increase the complexity and disrupt simplcity of the syntactic sugar provided by the programming languages. We try to identify some of the benefits of closures in this article.
Overview
Closures
The concept of closures has been around for a long time. Closures were first implemented fully as a language feature in 1960 in the programming language Scheme. They were used to implement the lexically scoped first-class functions (that is functions that are treated as first-class objects. A closure is the function value which is treated as an object and which contains references to the variable names in the lexical environment which was active when the closure was evaluated. In simple terms closures can be thought of as blocks of code which can be passed around as objects and which have access to the context in which they were first declared. This allows you to separate out the control structures, logical operators, loop operators etc. from the details of how they will be used. Closures will help you save the context of a function after it is executed, that is after some initialization is done. This context can be used at a later point in time.
In Ruby, for example, closures can be defined as lamba functions or as a new instance of Proc, as shown the following code snippet. The lambda function has access to the variable n
. The output which is printed, first with n = 1
and then with n = 2
shows that the closure captures the name bindings of the variables in the lexical environment active when the closure was evaluated.
n = 1 addn = lambda {|m| m + n} addn.call(1) # output is 2 n = 2 addn.call(1) # output is 3
Methods
Methods are one of the most important concepts in Object Oriented Programming paradigm. They are essential to facilitate the fundamental principle of OO programming, that is encapsulation. Methods are subroutines of a class and at runtime, have access to the variables and other data stored in the class instance. Methods define the behavior of the object when the class is instantiated.
Instance methods
Instance methods are bound to the first class object and have access to the variable and other data of the object. The instance methods receive an implicit 'this' pointer which points to the object on which the method was called.
Static methods
Some of the methods do not require the class to be instantiated for them to be called. They do not have access to the data for the class instance nor do they have access to the implicit pointers such as 'this'.
Special methods
Methods such as constructors and destructors are used for creation and removal of objects respectively. Almost all OO languages provide constructs for operator overloading which allow to customize behavior of operators for a class. Conversion operator methods allow the compiler to provide an object of a different class. Copy-assignment methods override the assignment operator for the objects of a class.
Accessor methods
Objects have some data that is not visible to the outside world. These data items are called private items and are visible only to the methods of the class. This feature is required to provide encapsulation. Accessor methods are used to provide access to these variables (state) from other parts of the program.
Practical significance of Closures
Dynamic Object Creation
In Object oriented languages, classes are used to represent real world objects. This is possible because, classes have their private members,private and public functions,etc which enable object orientation. Closures can support similar functionality with much more ease, without having to create Classes,Objects,etc. Hence Closures form one of the most handy yet powerful tools of dynamic languages. For Example,
myobj = (function () { var privatefunction = function () { alert('hello'); } var privateVariable = "Private" return { publicfunction : function () { privatefunction(); } } })(); //One way to access the object is : myobj.publicfunction();
The myobj closure contains a private function and a private variable which cannot be accessed by anybody outside myobj. It also provides an external interface using the function publicfunction(). You can simply access the method publicfunction() without actually instantiating any class or object.
This feature is useful in situations where one off objects are required. If the programming language does not have the ability to define closures, the programmer would need a separate class for such objects. Following example achieves the same goal as the example above, by defining a class in Java.
Class MyObj { private void privatefunction() { alert('hello'); } private String privateVariable = "Private" public void publicfunction() { privatefunction(); } };
We then need to instantiate the class using the new
operator in Java, which creates an instance and assigns it to myObject
.
MyObj myObject = new MyObj(); myObject.publicfunction();
This class needs to be compile separately and imported where the object of this class is required. This is not convenient in cases where one off instance of the class is required to perform a certain operation. Closures come in handy in such situations.
Provide APIs
Closures can be used to implement interfaces. When developing API libraries, we will have to often provide an interface definition and a set of functions that can be called upon it. Closures are perfectly suitable for this kind of implementation. E.g Suppose you are writing a JavaScript library for providing a set of Calendar APIs. The interface could be defined this way:
Calendar = (function () { var calendarName = "file.db" //Unique Identifier for this Calendar Object return { addCalendarEntry : function (entry) { //Operates on Calendar file.db (as specified by calendarName param) } } })();
A user of this API library could use it as:
Calendar.addCalendarEntry(calendarEntry);
without worrying about its initializtions, instantiation,etc.
Event Handling and Callbacks
Closures can be best used for Event handling in case of Asynchronous calls. This is especially true about AJAX and Web Service calls. E.g.
function asyncAPI(uniqueId, user_cb) { function callback(result) // This will be invoked with "result" once httpAsyncCall() completes { user_cb(result,uniqueId) } jsObj.httpAsyncCall(callback) //Actual Client-server call }
In this case, the function asyncAPI() immediately returns after making the httpAsyncCall to the server. Later, when httpAsyncCall() finishes, it will invoke the callback() function passed to it. But because of closures, the callback will continue to have access to the values of uniqueId and user_cb, even after asyncAPI returns. Hence it can invoke the appropriate user_cb() function once it gets the result.
Design Patterns
Closures can be used to implement Factory design pattern. E.g
function showHelp(help) { alert('showHelp'+help); } function makeHelpCallback(help) { //Factory function //Make some initializations return function() { showHelp(help); }; } var rubyHelp = makeHelpCallback("Ruby"); var jsHelp = makeHelpCallback("JavaScript"); //These handles can be passed to anybody and later invoked like: rubyHelp(); //Will pop up "help" specific to Ruby jsHelp(); //Will pop up "help" for JavaScript
Pre-processing computations
Closures can be used for pre-processing and storing the results of complex computations.
Dictionary = (function () { //does sorting of the entire word list sortWordList(); return { search : function (word) { //Searches for the given "word" in the sorted list of words } } })();
Here the function sortWordList() and the surrounding code (except the return statement) is executed the moment this JavaScript file is included (or parsed). Since sorting the entire word list could be a time consuming operation, this takes place before any request for searching arrives. Once this is done, anytime a call to search() can be executed efficiently on the already sorted list available to it.
Continuation passing style
Continuation Passing Style (CPS) is a style of programming used in functional programming languages in which no function returns a value. Instead, the languages which support continuations accept an explicit parameter which is a continuation function that is applied to the result of the called function. The following example taken from here demonstrates the use of continuation passing style in Scheme programming language to calculate a Pythagorean triplet.
(define (pyth& x y k) (*& x x (lambda (x2) (*& y y (lambda (y2) (+& x2 y2 (lambda (x2py2) (sqrt& x2py2 k))))))))
Here, k
is the extra parameter passed to the function. This is the continuation function. The result of a function is passed to the continuation function which then passes its result to the next continuation function and so on.
Deferred execution
Deferred execution allows you to control when a block of code will be executed. This is required in situations where the execution of code could potentially get lengthy or in cases where execution of the code is not always necessary. In the example given below, a block of code is passed to the function which then converts it into a closure and saves it for later execution. The closure is actually executed when the saved closure is invoked using the .call
method on the closure. The code is written in Ruby programming language which uses the lambda construct to define closures.
class Greeter def initialize(name) @saved = lambda {puts "Welcome, #{name}!"} end def greet() @saved.call end end grtr = Greeter.new "Harry" puts "Deferred Execution" grtr.greet # Outputs "Welcome, Harry!"
Some Disadvantages of using closures
- Closures capture variables and in some languages (such as JavaScript), chains of scope/contexts. As long as closures are active, this memory cannot be garbage collected. Hence excessive usage of Closures is not recommended especially in limited memory environments.
- Closures can also lead to memory leaks due to formation of circular references. This is not an issue in purely garbage collection systems. But in Garbage collection with reference counting, the system fails to identify a circular reference, hence leading to memory leaks.
References
http://groovy.329449.n5.nabble.com/Difference-between-Closure-and-Function-td334770.html
http://csharpindepth.com/Articles/Chapter5/Closures.aspx
http://livedocs.adobe.com/flex/3/html/help.html?content=03_Language_and_Syntax_21.html
http://www.javabeat.net/articles/62-closures-in-groovy-1.html
http://soft.vub.ac.be/amop/at/tutorial/multiparadigm
http://gafter.blogspot.com/2007/01/definition-of-closures.html
http://www.quora.com/Computer-Programming/What-are-the-disadvantages-of-closures
http://stackoverflow.com/questions/1305570/closures-why-are-they-so-useful
https://developer.mozilla.org/en/JavaScript/Guide/Closures
http://www.brooksandrus.com/blog/2008/04/03/taking-advantage-of-closures-in-ecmascript/
http://www.artima.com/intv/closures.html
http://www.ibm.com/developerworks/java/library/j-cb01097/index.html
http://en.wikipedia.org/wiki/Continuation-passing_style