Also known as lexical closing or simply closures.
Modern Pascal such as FPC/Delphi allow one to use Lexical Closures, a feature only available in select languages.
C/C++ for example does not have real lexical closures, hence why C/C++ is a language for wimps. Pointer tricks aside.
However in Pascal the power of Lexical Closures is limited - because nested inner functions are meant to disappear after the outer function ends execution.
Lisp, Modern Pascal, and other languages have lexical closures.
The Program:
{ This program shows why you shouldn't try and be clever with Lexical closures }
program RealProgrammersUseLexicalClosures;
{$APPTYPE CONSOLE}
uses
SysUtils,
Unit1 in 'Unit1.pas';
var
// counter object? No objects!
c: tcounter;
begin
MakeCounter(c); // create the fake object
writeln(c.getval);
c.increment;
writeln(c.getval); //access the value of the fake object
c.increment;
writeln(c.getval);
c.increment;
writeln(c.getval);
c.decrement;
writeln(c.getval);
c.setval(300); // set the value of a fake object
writeln(c.getval);
c.increment;
c.increment;
c.increment;
writeln(c.getval);
readln;
end.
The Unit:
unit Unit1;
interface
type
TCounter = record
{ public }
getval: function: integer;
increment: procedure;
setval: procedure(newval: integer);
end;
procedure makecounter(var AResult: Tcounter);
implementation
procedure MakeCounter(var AResult: TCounter);
const //assignable constants must be on
FValue: integer = 0;
{ public implementation }
function getval: integer;
begin
result:= FValue;
end;
procedure setval(newval: integer);
begin
FValue:= newval;
end;
procedure increment;
begin
inc(FValue);
end;
begin
AResult.getval:= @getval;
AResult.setval:= @setval;
AResult.increment:= @increment;
end;
end.
This is not correct programming in Pascal because these functions are pointing to possibly invalid memory. The outer function that hold these nested inner functions
has gone out of scope when we call the above pointers to nested functions. Because they have gone out of scope it can cause issues. Although the above program works it is not recommended practice.
Type checking in pascal will forbid this if no '@' is used. For example in Delphi:
AResult.getval:= getval;
AResult.setval:= setval;
AResult.increment:= increment;
Why Lisp style Lexical closures are powerful? They would be powerful because you can program in a way similar to object orientation (depends how you think about it).
Other language advocates such as Lisp programmers argue that lexical closures are the way to program. Local nested functions in Lisp are not the same as in pascal.
The way I am used to thinking (imperative, scope) it makes sense for me not to program the tricky Lisp way, because where is my local function after a certain part of the code has already been passed? My local function in my imperative mind is not accessible outside the function! But in the above program I access the local nested function outside the holding (outer) function! Advanced lisp style closures do not make sense to an imperative programmer if he properly considers scope and entry/exit points of the procedure.
A Lisp programmer thinks differently about scope and nested functions.
So Lexical Closures are available in Pascal, but not as powerful ones as in Lisp. Although Lexical Closures are powerful in Lisp since nested functions do not disappear like in Pascal, it is questionable whether or not lisp style Lexical Closures are really all that spectacular for program maintenance. They are tricky, and clever.
It does depend on what programming language you are used to programming in - in Pascal we are more used to considering scope, private, public, local, global, entry points, exit points - whereas a Lisp programmer is more used to a partially so called functional style of programming (no functional language is purely functional though, even Haskell).
What is a closure? The way I define a closure is when you enclose or capture some code (lex) and use. Precisely it is enclosing code, hence the name. In the above program (even though it is an incorrect program) we have enclosed some local nested functions. We have enclosed them or captured for later use. When we use them later, we are taking advantage of our enclosure that we performed beforehand. However, as stated before, it is an incorrect program in Pascal and is confusing to the programmer who is used to scope, private, global, public style thinking.
If lexical closures are so powerful, why do we have to think in terms of global, local, public? Why couldn't we have lexical closures in Pascal? The most probable reason that it has not caught on is because it is confusing to the style of thinking that we are used to. How can we capture the scope of a function if that function is already dead and gone? How can we use inner functions of an outer function if that outer function has already executed?
This may be the reason people have a hard time taking to Lisp. It may be too confusing, similar to the way recursion is confusing.
Tieing or associated a nest of functions to an outer function doesn't make as much sense (to most of us) as tieing the functions to an object, class, structure, or record. What's the function nest got to do with the function? A procedure from an imperative/oop perspective should be attached to a box, circle, pot, pan - some sort of structure.
Methods are attached to objects, and procedure pointers are atached to structs and records. Should methods be attached to other functions and enclosed? I think the reason this is not successful in most languages is because it doesn't make human sense. Basically I'm defending OO (object orientation), and I'm not even a super OO advocate or fan myself! I think enclosing functions or attaching them (associating them) to some sort of structure is useful in many cases - and OO does have it's place - but lexical closures like in Lisp may be too hard to understand and grasp on to for many people.
According to the direction programming languages are heading in the present (Java, C++, D, D++?, Modern Pascal, Ada, PHP), associating a nest of functions with a function doesn't seem to make as much sense as associating a nest of methods to an object or some other form of noun or structure. Rather than associating a nest of methods with a verb, action, or function, the current popular programming languages associate a nest of methods with a noun, structure, or object.
|