There is something I really don't understand about the encapsulation and the closure. I believe that the encapsulation is what can not be changed unless it is changed by the code. But I can't really understand when I am asked to explain how the closure and the encapsulation are applied on a code.
For example :
(define new-cercle #f)
(let ((n 0))
(set! new-cercle
(lambda (rayon)
(begin
(set! n (+ n 1))
(lambda (msg)
(cond ((eq? msg ’circonference)
(* 2 3.14 rayon))
((eq? msg ’surface)
(* 3.14 rayon rayon))
((eq? msg ’nb-cercles)
n)))))))
The n
is encapsulated, right? So the question is: explain how the encapsulation and the closure are applied on this code.
Another thing I do not understand is why the let
has to be above the lambda here? Why when I put it below the lambda
, the function doesn't work well and there is no accumulator ?
(define acc
(let ((n 1))
(lambda (x)
(set! n (* n x))
n)))
I hope someone would explain to me this in an easy way because when I google'd it, honestly I didn't understand anything with the complicated examples most topics have.
Encapsulation the name of a pattern involving any situation in which some related items are put together into a container and then travel with that container, and are referenced through some access mechanism on that container. The items could be run-time values, or compile-time identifiers or whatever. An object made up of multiple fields encapsulates fields: a cons
cell encapsulates car
and cdr
. A class encapsulates slots. In some object systems, methods also. Compilation units encapsulate their global definitions such as functions and variables.
The popular use of "encapsulate" in OOP refers to defining a class as a unit which contains the definition of data, together with the methods which operate on it: the code and data are one "capsule". (The Common Lisp object system isn't like this: methods are not encapsulated in classes.)
A closure is something else, something very specific: it is a body of program code, together with its lexical environment, reified into an object of function type. The body of the closure, when invoked, has visibility to two groups of names: the closure's function parameters, and the surrounding names in the lexical scope where the closure is created. A closure is an example of encapsulation: it encapsulates the body of code together with the lexical scope. The only means of access into the capsule is through the function: the function is like a "method", and the elements of the captured lexical environment are like "slots" in an object.
(By combining code and data, Lisp closures resemble the popular notion of encapsulation more than do Lisp class objects.)
About that funny word: in computer science, to "reify" some aspect of a program is to take something which is not a first class object, and somehow turn it into one.
Almost any recognizable concept which is applicable to the understanding of a program can potentially be reified. (Someone clever just has to come up with a sensible proposal about how.)
For instance, the entire future computation at a given point of execution can be reified, and the resulting object is called a continuation (and undelimited continuation, more precisely).
When the continuation operator captures a future computation, that future becomes hypothetical: it doesn't actually happen (doesn't execute). Instead, an alternative future executes in which the continuation is returned to the operator's caller, or passed into a function which the caller designates. The code which now has this continuation in its grasp can use it to explicitly invoke the original, captured future, as if it were a function. Or choose not to do that. In other words, program control flow (execute this block or don't execute this, or execute it several times) has become a function object (call this function or don't call it, or call it several times).
Objects are another example of reification: the reification of modules. Old-fashioned programs are divided into modules which have global functions and global variables. This "module" structure is a concept that we can recognize in a program and usefully apply in describing such programs. It is susceptible to reification: we can imagine, what if we had a run-time object which is "module", having all those same attributes: namely containing functions and data? And, presto: object-based programming is born, with advantages like multiple instantiation of the same module, possible because the variables are no longer global.
cercle
and rayon
:Firstly, new-cercle
behaves like a constructor for objects: it is a global function that can be called from anywhere. It maintains a count of how many objects have been constructed. Only that function can access the counter, so it is encapsulated. (Actually not only that function can access it, but also the closures representing the circle instances!) This is an example of module-like encapsulation. It simulates modules, like in the language Modula-2 and similar, such as C language translation units with static
variables at file scope.
When we call new-cercle
we must supply an argument for the rayon
parameter. An object is produced and returned. That object happens to be a function produced as a lexical closure. This closure had captured the rayon
parameter, thereby encapsulating this value: the object knows its own radius. We can call new-cercle
repeatedly, and obtain different instances of circles, each carrying its own rayon
. This rayon
is not externally visible; it is packaged inside the closure, and is only visible to that function.
We gain access into the rayon-container indirectly, through a "message" API on the container. We can call the function with the message symbol surface
, and it replies by returning the surface area. None of the currently available messages reveals rayon
directly, but we could provide an accessor message for that, and even a message to change the radius. There is even a message to access the shared variable n
, the count of circles, which behaves like a class variable in an object system (static slot): any instance of a circle can report how many circles have been constructed. (Note that this count doens't inform us how many circles currently exist: it doesn't decrement when a circle becomes garbage and is reclaimed: there is no finalization).
In any case, we clearly have a container whose contents are not accessible except through an interface. That container binds together code and data, so it is not only encapsulation, but arguably encapsulation in the popular OOP sense.
You're perhaps having difficulties because in the trivial case, some differences go away. For example both (let ((n 1)) (lambda (x) n))
and (lambda (x) (let ((n 1)) n)
give you basically the same function.
In your example
(define acc (let ((n 1))
(lambda (x) (set! n (* n x)) n)))
the ordering of let
and lambda
is significant. If you interchange them to (lambda (x) (let ((n 1)) ...
then every time you call this function n
will again be bound to 1
. Instead you want there to be some location n
that starts out with value 1
and can be modified by your function and does not go away when your function is done, which is what you get when you have (let ((n 1)) (lambda (x) (set! n ...
.
The function constructed by the inner lambda
captures the use of the outer n
and holds on to its location for as long as it itself lives. It also encapsulates n
as nothing else can refer to it but this function. We also say that the function is closed by the surrounding binding of n
, and that the function is a closure (of n
).
Reading about lexical scope might help you as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With