CSC/ECE 517 Fall 2011/ch1 1d sr
Wiki Chapter: CSC/ECE 517 Fall 2011/ch1 1d sr
Introduction
Closures
What are Closures
Let us define what a closure as a programming construct means.
A Closure is basically a function or a method (or a block, in the context of ruby) that has the following two properties -
- One can pass it around like an object or as a value parameter to other functions/methods/blocks
- It takes the snapshot and effectively "remembers" the values of all the variables that were in scope when the function was created and it is because of this property that it is able to access those variables when it is called even though they may no longer be in scope. [1]
In order for a programming language to be able to support closures, it must support the notion of "first class functions." A first class function can be treated like an object in that you can pass it around as parameter or you can store it in collections. [2] Since a closure can be passed around as a value before calling it, it can be called in a completely different scope than the one in which it was created and it is because of this that it needs to retain the knowledge of the lexical environment in which it was defined. [3]
Let us articulate in detail what "saving a lexical environment of its definition" means when it comes to a closure. This is required because the ability of a language to provide "props" for saving lexical context of a function definition determines the level of difficulty and effectively feasibility of implementing closures in languages that do not support closures directly viz. the Statically Typed Languages like C, C++, Java. One way of doing this would be to make a copy of all the variables that are needed when a closure was defined. Alternatively, the lifetime of such variables can be extended not by explicitly copying them but by retaining a reference to them, thus making them not eligible for garbage collection. [4]
Why Closures
- For Functional languages [5], which themselves are essentially stateless, closures offer a way to store some kind of state at least for as long as the closure lives.
- Closures help functional languages to be terse in expressing logic. Closures also offer a very succinct way of performing some neat programmatic operations viz. if a closure modifies the value of a variable, it will retain the new value the next time the closure was invoked.
- Higher order, special purpose functions like select, inject in Ruby, which do a lot of useful stuff with very little code are possible only because the language supports closures.
- Currying of functions in languages like Ruby is only made possible through the use of closures.
- For procedural or imperative languages, the argument for closures is a twofold one. Procedural languages already have mechanism to store state via the use of global variables, static variables etc. but closures offer a way to pass around units of computation that can be executed later. The function pointers and the callback function ideology in C is centered around this motivation. For object-oriented imperative languages like C++ or the Objective-C, closures provide for the lack of a syntactically light-weight way to define simple object functions for the effective use of several generic algorithms like those offered by Standard Template Library.
Closures in Dynamically Typed Languages
The programming language which does majority of its type-checking at run-time instead of compile-time is called as a dynamically typed language [6]. In dynamic typing, values have types but variables do not. Hence a variable can refer to a value of any type. This important property of dynamically typed languages offers itself as a convenient way of implementing and supporting closures. A variable will point to a block of code and the associated lexical environment. A special-purpose programming construct called "lambda" is used in some languages to express anonymous functions i.e functions which are not bound to a name at runtime. Lambda is used to generate new functions dynamically. The concept of "lambda" has been derived from the lambda calculus in the functional programming [7].
Example use of Closures in Dynamically Typed Langauges
We will now take a look at examples of usage of closures in some of the Dynamically typed languages. We will not be articulating on all the aspects and subtleties of closures in a particular language (in Ruby, for example, there are Seven distinct ways of implementing closure or closure-like structures[8]), but we will constrain ourselves in explaining a particular and commonly used way of closures.
Example in Ruby
In Ruby, for all intents and purposes, "Blocks" [9] are closures. They are closed with respect to the variables defined in the context in which they were created, regardless of the context in which they are called. The subtle difference is that a "Block" can not be passed as a specific named parameter and "yield" is the only way by which control can be given to the block that was passed to the method in which yield is called.
class ClosureTest def initialize(x) @x = x end def call_closure(ClosureBlock) ClosureBlock.call @x end end a = 100 ClosureBlock = lambda {|x| puts x+a} ClosureObj = ClosureTest.new(10) ClosureObj.call_closure(ClosureBlock) # This will output "110" a = 200 ClosureObj.call_closure(ClosureBlock) # This will output "210"
As seen in the above example, "ClosureBlock" is a closure. The use of lambda construct means that the ClosureBlock variable would be of type "Proc". When ClosureBlock is defined, the variable "a" is in its scope and hence it will remember the value that "a" was bound to. However when ClosureObj, an object of class ClosureTest calls "call_closure" method with ClosureObj passed as a parameter (closures can be passed around as objects), it will remember the value of "a" even though "a" is now no longer in scope and will print "100+10" as the answer. On the similar notes, when "a" is modified and call_closure is invoked again, the ClosureBlock will up the value of "a" in the lexical context in which ClosureBlock was created i.e. it will pick up the latest value of "a".
Example in JS
Example in Python
Closures in Statically Typed Languages
The Problem
Limitations of Statically Typed Languages
Lexical Scope
Functions not as first class citizens of the language
Implementing Closures in Statically Typed Languages
C : function pointers
C++ : function objects
Java : anonymous inner classes
Closures and Static Scoping
(Explanation)
Case study of Scheme
References
1. Matz discussion on Closures