CSC/ECE 517 Fall 2011/ch1 1d sr

From Expertiza_Wiki
Jump to navigation Jump to search

Wiki Chapter: CSC/ECE 517 Fall 2011/ch1 1d sr

Closures in statically typed languages. Most languages that implement closures are dynamically typed. It is a challenge to implement closures in a statically typed language. Explain why, and cover attempts to mix the two. Consider also closures and static scoping, as in Scheme.

Introduction

Closures

What are Closures?

A closure is basically a function or a method (or a block, in the context of ruby) that has the following two properties:

  • It is a first-class function, meaning it can be passed around like an object or as a value parameter to other functions/methods/blocks
  • It saves its lexical environment, meaning it captures the variables within scope at its creation and maintains them even if they later go out of 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 is a function that can be treated like an object, such as passing it as a parameter or returning the function as the result of another method. [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]

Why Closures

  • For functional languages, which themselves are essentially stateless, closures offer a way to store some kind of state at least for as long as the closure lives. [4]
  • Closures help functional languages to be terse in expressing logic. Closures also offer a very succinct way of performing some neat programmatic operations i.e. if a closure modifies the value of a variable, it will retain the new value the next time the closure was invoked. [5]
  • Higher order, special purpose functions like select and 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. Currying is taking a function and one or more of its parameters to produce a new function with fewer parameters, which can be useful for human readability and coding.[6]
  • 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

A dynamically typed language is a programming language which does majority of its type-checking at run-time instead of compile-time [7]. 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. For closures, a variable can 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 [8].

Example use of Closures in Dynamically Typed Langauges

The following are examples of closure usage in some of the dynamically typed languages. These examples cover a particular and common usage of closures, but 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[9]) will not be covered here.

Example in Ruby

In Ruby, for all intents and purposes, "Blocks" [10] 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 pick 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

Challenges For Closures in Statically Typed Languages

Closures are not usually implemented in statically typed languages, due to their nature. The following are some obstacles statically typed languages must overcome:

  • While dynamically typed languages allow for any variable to be associated with any type, static languages require types declared at compile time. This would require a specific type for closures if they were implemented in a statically typed language.[11]
  • Many statically typed languages keep track of in-scope variables with a stack-based system: variables are pushed on when entering a scope and popped when exiting a scope. This can be a challenge for implementing closures in statically typed languages, since closures must maintain access to variables even when they go out of scope.

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

  • The concept of closures requires that a block of code along with the lexical context of its creation needs to be saved. C's context is based on the local variables which are created on the stack frame corresponding to the function currently being executed, the registers of the CPU which might store specific values and the global variables. Anything other than this is handled by the dynamic memory allocation provided by malloc.
  • The programming construct of "function pointers" is a useful tool for implementing closures in C. A function pointer is, as the name suggests, a pointer which stores address of any function. One can pass this function pointer holding the address of a particular function to another function as a parameter. The function which receives the function pointer as a parameter can now "call" or execute the function pointed to by the function pointer. This, in its most rudimentary form, is the basic technique by which C can pass along blocks of code to another functions.
  • However the definition of closure also mandates that the lexical context at the time of its creation need also be saved and accessible later. This proves to be a painful task and one needs a logic to provide this functionality in the most concise and acceptable manner. One way to do this would be to provide a programmer defined struct which holds the values of all the variables which were "in scope" of the function that is to be treated as closure.
  • Note here that the struct variable which stores the values of all the variables in the lexical context of the function under consideration must not be "statically" allocated i.e. it must not be allocated on the stack of the method currently being executed. So this context saving structure variable needs to be allocated from the heap using malloc and the pointer to this "context structure" will need to be passed along with the function pointer to any functions that need to execute this later. However, note that because of the inherent nature of "statically" typed behavior, one would have to associate a particular function pointer signature and hence this combination of function pointer and user data struct pointer is not exactly type independent.
  • "Type independence" in this context means that a function block would have to be closely tied with its signature like the data types of the parameters that the function expects, the data type of the return value that the function should return etc. So to create a different closure corresponding to a function of different signature would need the definition of another function pointer with compatible signature.

C++ : function objects

Java : anonymous inner classes

Java can provide similar functionality to closures through anonymous inner classes, though there are limitations. For simulating closures, the local variables from enclosing scopes used in the anonymous class must be declared final. This is because variables are not resolved in the enclosing scope, but in the class's scope. Using Java's anonymous inner classes in this way is also not as simple to implement code-wise as it is to use closures in dynamically typed languages like Ruby. [12]

James Gosling, regarded as the inventor of Java, stated that "closures were left out of Java initially more because of time pressures than anything else." He also explains inner classes being added to Java to alleviate the lack of closures, but that this was not a real solution. Gosling feels he should have gone all the way to implementing closures back then. [13] Proposals are currently being made for Java to include closures as a feature. [14]

Closures and Static Scoping

(Explanation)

Case study of Scheme

References

1. Matz discussion on Closures

2. Closures in C++

3. Closures in C

4. Closures in Java

External Links

1

2

3

4

5

6

7

8

9

RESOURCES TO LOOK THROUGH IN WORKING ON THE WIKI - KURT

[15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28]