Previous Page Next Page

14.3. Anonymous Subroutines, Closures, and Privacy

One of the problems with the object-oriented examples we have used thus far is that a user can manipulate the object directly once he gets a reference to it. Even if he is supposed to use the methods provided by the module, there is nothing to stop him from accessing the object's data directly, since Perl does not specifically provide a private section for the class data. But for those who feel this lack of guaranteed privacy is an affront to the object-oriented approach, the paranoid, the sensible, etc., Perl provides several solutions. One of them is the use of closures.

14.3.1. What Is a Closure?

Larry Wall describes closures as just anonymous subroutines with an attitude.[8] Barrie Slaymaker calls closures "inside-out objects": objects are data that have some subroutines attached to them, and closures are subroutines that have some data attached to them.

[8] Wall, L., Christianson, T., and Orwant, J., Programming Perl, 3rd ed., O'Reilly & Associates: Sebastopol, CA, 2000, p. 262.

A closure is an anonymous subroutine that has access to "my" (lexical) variables even if it is called from outside the block where the variables were defined and it seems as though those variables should no longer be in scope. The subroutine clings to the lexical variables it references. Each time the subroutine is called via its reference, a new lexical variable with the same name is created for that call. The lexical variables stay in scope until they are no longer being referenced.

Example 14.14.

(The Script)
1   my $name="Tommy";

2   {   my $name = "Grandfather";  # Lexical variables
3       my $age = 86;
4       $ref = sub{ return "$name is $age.\n"; }  # anonymous subroutine
    }
5       print "$name is back\n";
6       print &{$ref}; # Call to subroutine outside the block

(Output)
5       Tommy is back.
6       Grandfather is 86.

Example

  1. The lexical variable $name is assigned Tommy. The variable is visible from here to the end of the file.

  2. A block is entered. A new lexical variable, $name, is assigned Grandfather. It is visible from here to the end of its block.

  3. Another lexical variable, $age, is defined. It is visible from here to the end of the enclosing block.

  4. An anonymous subroutine is defined within the same block as the two lexical variables (my variables), $name and $age. The address of the subroutine is assigned to $ref. The subroutine has access to those variables even if it is called from outside the block. The subroutine is called a closure because the variables referenced within the subroutine are enclosed there until they are no longer needed.

  5. The value of $name, Tommy, is now visible.

  6. The anonymous subroutine is called via the pointer $ref. The lexical variables are still available even though they appear to be out of scope. They remain in scope because the reference still needs access to them. Perl doesn't clean up the variables until they are no longer referenced.

Example 14.15.

(The Script)
    # Closure
1      sub paint {
2         my $color = shift;    # @_ array is shifted
3         my $ref = sub {      # Pointer to an anonymous subroutine
4            my $object=shift;
5            print "Paint the $object $color.\n"; # $color still
                                                  # in scope
          };
6      return $ref;    # Returns a closure
       }

7      my $p1=paint("red");   #  Creates a closure
       my $p2=paint("blue");  #  Creates a closure

8      $p1->("flower");  # Call to anonymous subroutine
9      $p2->("sky");

(Output)
Paint the flower red.
Paint the sky blue.

Explanation

  1. The paint() subroutine is defined.

  2. The lexical scalar $color is assigned the value shifted from the @_ array.

  3. The value in $ref is assigned the address of an anonymous subroutine.

  4. The anonymous subroutine takes one argument from the @_ array. In this example, the value of object will be "flower" the first time this subroutine is called and "sky" the next time. See lines 8 and 9.

  5. Here is where we see a closure in action. The lexical variable $color is still in scope. The lexical variable $color doesn't go out of scope even after the subroutine called paint() is called and exited, because the anonymous subroutine still needs it.

  6. $color is still available here even though the paint() function was called and exited.

  7. The paint() subroutine returns a reference to the anonymous subroutine. The reference forms the closure; it keeps the lexical variables, in this case $color, around until they are no longer being referenced.

  8. The paint() subroutine is called twice with different arguments. Each time paint() is called, Perl creates a new lexical scalar, $color, with it own value. The $color variable gets wrapped up in the closure that is returned. So $p1 encloses one $color, which is initialized to "red", and $p2 encloses a totally different $color, initialized to "blue".

  9. The pointers $p1 and $p2 are references to the anonymous subroutine defined on line 3. They have formed a "closure" around the variable $color defines in paint() and will have access to their own copy of that variable until it is no longer being referenced.

14.3.2. Closures and Objects

Closures provide a way to encapsulate the object's data and thereby prevent the user from directly accessing the object. This can be done by defining the constructor with the object's data and an anonymous subroutine that will act as the closure. The anonymous subroutine will be the only way to set and get data for the object. Instead of blessing the data (e.g., anonymous hash into the class), the anonymous subroutine will be blessed. The pointer to the anonymous subroutine will be returned and serve as the only way to access the private data defined in the constructor. The blessed anonymous subroutine will have access to the object's data because it was declared within the same lexical scope. It encapsulates the data with the subroutine; i.e., forms a closure. As long as the anonymous subroutine refers to the object's data, the data will be accessible.

Example 14.16 will demonstrate how to use a closure to encapsulate the data for an object by following these steps.

1.
A constructor method is defined for a Student class. The constructor will define an empty anonymous hash that will be used to set properties for each new Student object, a global class variable to keep track of the number of students, and an anonymous subroutine to encapsulate the data to be assigned to and retrieved from the object. The blessing will return a pointer to the anonymous subroutine.

2.
The instance methods will be defined for the object for setting and getting the data. Instead of getting back a pointer to the object's data, these methods will get back a pointer to the anonymous subroutine. The only way they can access the data is by calling this anonymous subroutine with the appropriate arguments.

3.
A destructor method will be defined to display each Student object as it is being destroyed.

Example 14.16.

 1   package Student;
 2   sub new { #  Constructor
        my $class = shift;
 3      my $data={};
 4      our $students;

 5      my $ref = sub { # Closure
 6         my ($access_type, $key, $value) = @_;

 7         if ($access_type eq "set"){
              $data->{$key} = $value;  # $data still available here
           }
           elsif ($access_type eq "get"){
              return $data->{$key};
           }
           elsif ($access_type eq "keys"){
              return (keys %{$data});
           }
 8         elsif ($access_type eq "destroy"){
 9            $students--;
              return $students;
           }
           else{
              die "Access type should be set or get";
           }
 10        print "New student created, we have ", ++$students,
             " students.\n";
11         bless ($ref, $class);  # bless anonymous subroutine
       }
    } # End constructor
12  sub set{
13     my ($self,$key,$value) = @_; # $self references anonymous sub
14     $self->("set",$key,$value);
    }
15  sub get{
       my ($self,$key) = @_;
       return $self->("get", $key);
    }
16  sub display{
       my $self = shift;
       my @keys = $self->("keys");
       @keys=reverse(@keys);
       foreach my $key (@keys){
          my $value = $self->("get",$key);
          printf "%-25s%-5s:%-20s\n",$self, $key,$value ;
       }
       print "\n";
    }
17  sub DESTROY{
       my $self = shift;
       print "Object going out of scope:\n";
       print "Students remain: ", $self->("destroy"), "\n";
    }

1;

					  

Explanation

  1. The package Student is declared.

  2. The constructor will be used to create the object.

  3. A reference, $data, to an anonymous hash is declared. This will be used to assign properties to the object.

  4. The global class variable $student will be used to count the number of Student objects created by this script.

  5. Here is the closure, a pointer to an anonymous subroutine. This subroutine will be blessed into the class and will be used to set and get the data for the Student object.

  6. The subroutine takes three arguments, the access type, which is "set", "get", "keys", or "destroy"; a key for the object; and a value for the object.

  7. If the access type is "set", then a property is set for the object's data. When the set() method is called by the user, it gets a pointer to this anonymous subroutine and calls it from line 15.

  8. If the access type is "destroy", the DESTROY method has been called; i.e., a Student object is going out of scope.

  9. Each time an object is removed, the value of the class variable $student is decremented by 1.

  10. Each time a new Student object is created, the class variable $student is incremented by 1.

  11. The anonymous subroutine, referenced by $ref, is blessed into the Student class.

  12. This method is used to put data into the object; i.e., assign properties to the student.

  13. Its first parameter, $self, is a pointer to the anonymous subroutine blessed into the class, then a key, and a value. The data for the object has been "enclosed" in the anonymous subroutine.

  14. When the anonymous subroutine is called, it gets three arguments, the access type, "set" a key; and a value to be assigned to the object. This is the only way the user can "set" data for the Student object and will "stick around" until there is no longer any reference to it.

  15. The get method will call the closure with a specified access method, "get", and a key. It will return one value to the caller.

  16. This method is used to fetch data in the Student object. It calls the closure on line 5 with the key that will be used to get the value for the object. This is the only way to "get" data from the object.

  17. This method will display all the object's data by calling the closure to get a list of all the keys in the object's hash, and then from within a foreach loop, the list of keys is used to display all of the object's data.

  18. Perl's special DESTROY method is called when an object is about to be removed from memory; i.e., when it goes out of scope or the program ends. When this method is called, it will, in turn, call the closure and send it the access type, "destroy". See line 8.

 

User of the Module
Example 14.17.

  use lib("lib");
1 use Student;

2 $ptr1 = Student->new();  # Create new students
  $ptr2 = Student->new();
  $ptr3 = Student->new();

3 $ptr1->set("Name", "Jody Rogers");  # Set data for object
  $ptr1->set("Major", "Law");

  $ptr2->set("Name", "Christian Dobbins");
  $ptr2->set("Major", "Drama");

  $ptr3->set("Name", "Tina Savage");
  $ptr3->set("Major", "Art");
4 $ptr1->display();  # Get all data for object
  $ptr2->display();
  $ptr3->display();

5 print "\nThe major for ", $ptr1->get("Name"),
      " is ", $ptr1->get("Major"), ".\n\n";

(The Output)
 New student created, we have 1 students.
 New student created, we have 2 students.
 New student created, we have 3 students.

Student=CODE(0x225420)   Name :Jody Rogers
Student=CODE(0x225420)   Major:Law

Student=CODE(0x183192c)  Name :Christian Dobbins
Student=CODE(0x183192c)  Major:Drama

Student=CODE(0x1831a04)  Name :Tina Savage
Student=CODE(0x1831a04)  Major:Art

The major for Jody Rogers is Law.

Object going out of scope:
Students remain: 2
Object going out of scope:
Students remain: 1
Object going out of scope:
Students remain: 0

--------Try to get direct Access----------------------------------
  use lib("lib");
  use Student;

$ptr1 = Student->new();  # Create new students
$ptr2 = Student->new();
$ptr3 = Student->new();
$ptr1->("Name", "Jody Rogers");  # Direct Access Not Allowed
$ptr1->set("Name", "Jody Rogers");
$ptr1->set("Major", "Law");
 --------------------------------------------------
(Output)
New student created, we have 1 students.
New student created, we have 2 students.
New student created, we have 3 students.
Access type should be set or get at Student.pm line 25.
...

					  

 

Previous Page Next Page