1. Perl
  2. Subroutine
  3. here

How to create a closure

Learn how to create "closures" in Perl.

Closure definition

I will explain the definition of closure using code.

use strict;
use warnings;

# The simplest closure
{
  # Variable generation
  my $var = 5;
  sub var {
    return $var;
  }
}
# Because it is referenced by the var subroutine even after the scope ends
# $var continues to exist.
# Only the var subroutine can see $var.

print "1: The simplest closure\n";
print var (), "\n";

Closure definition

The condition for being a closure is that the subroutine reference a lexical variable that is out of its scope. (In this example, var refers to $var outside its scope.)

The definition of closure is ambiguous depending on the person and the situation. The var subroutine is sometimes called a closure, and the "environment containing the variable $var referenced by the var subroutine" is sometimes called a closure.

When creating a subroutine, I wrote that I should always take a variable as an argument and not refer to an out-of-scope variable, but closures are one exception.

Please refer to the following article for the scope of Perl.

Closure features

{
  # Variable generation
  my $var = 5;
  sub var {
    return $var;
  }
}

Since $var is referenced in the var subroutine, it is not deleted from memory when the scope ends. (Lexical variable are destroyed from memory when they are out of scope and are not referenced anywhere.)

When the scope ends, $var is no longer visible outside the scope. (The var subroutine is not affected by the scope and is visible from outside the scope.)

Only the var subroutine can see $var. After the scope is finished, the value of $var can only be accessed through the var subroutine.

Closure image

| - -- -- -- --- Scope (closure) - -- -- -- --- |
| |
| | - -- --- | | - -- -- -- --- | | | - -- -- -- - |
| | | Accessible | | accessible | |
| | Data | <- -- -- -- -| Subroutines | <- -- -- -- -- | Out of scope |
| | | | | | | |
| | - -- --- | | - -- -- -- --- | | | - -- -- -- - |
| |
| - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- |

                   Data is inaccessible from outside the scope

Data can only be accessed from outside the scope through subroutine. You cannot access the data directly.

Create closures using "function generator"

Let's create a closure using a function generator.

use strict;
use warnings;

# Generate a timer.
print "1: Generate closure with function generator (timer).\n";
my $timer1 = create_timer ();
sleep 1;
# Generate another timer after 1 second
my $timer2 = create_timer ();

# Stop for 1 second
sleep 1;

print'$timer1 elapsed seconds:';
print $timer1->(), "\n";

print'$timer2 elapsed seconds:';
print $timer2->(), "\n\n";


# Function generator that generates closures that know the elapsed time
sub create_timer {
  # time() is the time when the function generator was called
  my $time = time();
  
  return sub {
    # time() is the generated subroutine
    # The time it was called. time()-with $time
    # You can know the elapsed time.
    return time()-$time;
  }
}

Function generator for generating closures

sub create_timer {
  # time() is the time when the function generator was called
  my $time = time();

  # time() is the generated subroutine
  # The time it was called. time()-with $time
  # You can know the elapsed time.
  return sub {
    return time()-$time;
  }
}

A function generator is a subroutine that creates a suboutin. You can use the function generator to generate multiple closures.

Each generated closure has its own data. In this example, the value of $time is created in memory for each closure. The return value is a reference to the subroutine. This is because Perl does not allow the subroutine itself to be the return value.

Call a function generator to create a closure

my $timer1 = create_timer ();
my $timer2 = create_timer ();

The return value serves as a reference to the subroutine. This subroutine is a closure and has its own environment (variable referenced in the subroutine and the definition of the subroutine) in memory.

Call closure

$timer1->();

Now that we have a reference to the subroutine, we dereference it and call it.

How to use closures

Closures allow you to concisely describe the changes between the "state at the time of reference" and the "state at the moment".

You can do the same thing by creating a class, but if you don't want to create a class, use closures. (If you create one class, it will take more time to manage the class. If you do not want to create a class for simple description like this example, use closure.)

"Initialize variable" referenced by closures

This is an example to initialize the variable referenced by the closure.

use strict;
use warnings;

# Pass it as an argument to the function generator.
print "1: Initialize the variable referenced by the closure.\n";
my $sign_inversion = create_sign_checker (1);

if ($sign_inversion->(-2)) {
  # If the sign is inverted
  print "-2 has the sign inverted when viewed from 1.\n";
}

# Function generator that generates closures to see if the sign is only inverted
sub create_sign_checker {
  # Initialized when you create a closure.
  my $num1 = shift;
    
  return sub {
    # Argument when calling the closure.
    my $num2 = shift;
    
    if ($num1 * $num2 > 0) {
      # If not flipped
      return 0;
    }
    elsif ($num1 * $num2 < 0) {
      # If flipped
      return 1;
    }
    else {
      # Returns undef if either is 0
      return;
    }
  }
}

Initialize variable referenced by closures

# Generate closures. (Initialize the variable referenced by the closure with 1)
my $sign_inversion = create_sign_checker (1);

# Call closure
$sign_inversion->(-2);

# Function generator to generate closures
sub create_sign_checker {
  # Initialized when you create a closure.
  my $num1 = shif
t;
    
  # Closure definition
  return sub {
    # Argument when calling the closure.
    my $num2 = shift;
    # ...
  }
}

When you create a closure, you can initialize the variable that the closure reference. Pass it as an argument when creating the closure. Take it as an argument to the function generator and set it in a variable.

Explain the concept of closures in an easy - to - understand way in comparison with classes

The concept of closures is vague and difficult to capture contours. I would like to explain it so that I can understand it as much as possible.

What is a closure

  1. A closure is a set of data and functions that manipulate that data.
  2. The data is encapsulated and only the functions inside the closure can manipulate the data.
  3. Data can be initialized.
  4. You can create individual instances for each piece of data.
  5. The function is called from the instance.

Next, about classes in object orientation.

What is a class?

  1. A class is a set of data and functions that manipulate that data.
  2. The data is encapsulated and only the functions inside the class can manipulate the data.
  3. Data can be initialized.
  4. You can create individual instances for each piece of data.
  5. The function is called from the instance.

What you should be aware of here is that classes and closures are actually the same thing. In Perl implementations, classes and closures differ in their grammatical writing. However, they are functionally the same. Closures are easy to understand if you think of them as simple classes. (The concept of closures is a bit broader, but at first I understand it's a simple class.)

Differences between classes and closures

  1. Closures do not have a class name. By calling new my $ins = Class->new ;, instead of creating an instance, create an instance in the form of my $ins = closure_maker () ;.
  2. Closures cannot be inherited. It cannot be inherited like a class.

Let's implement a function (counter) for counting using classes and closures.

Implement the counter in a class

use strict;
use warnings;

package Counter;
sub new {
  my $class = shift;
  my $self = {};
  $self->{cnt} = shift;
  bless $self, $class;
}

sub add_count {
  my $self = shift;
  $self->{cnt}++;
}

sub count {
  my $self = shift;
  return $self->{cnt};
}

package main;
my $cnt = Counter->new(10);

print "1: Class-based counter implementation\n";
print "Before counting:", $cnt->count, "\n";
$cnt->add_count;
print "after counting:", $cnt->count, "\n\n";

Implement the counter with closures

use strict;
use warnings;

# Closure
sub make_counter {
  my $self = {};
  $self->{cnt} = shift;
   
  # Closures implement methods using reference to subroutine.
  my $funcs = {};
  $funcs->{add_count} = sub {
    $self->{cnt}++;
  };
   
   $funcs->{count} = sub {
     return $self->{cnt};
   };
   return $funcs;
}

my $cnt = make_counter (10);

print "2: Implementation of counters with closures\n";
print "Before counting:". $Cnt->{count}->() . "\n";
$cnt->{add_count}->();
print "after counting:". $Cnt->{count}->() . "\n";

As you can see, classes and closures are very similar. Next time, I will explain the code.

Visual image of classes and closures

# # class
| - -- - Class - -- - |
| ・ Data |
| ・ Data manipulation functions |
| - -- -- -- -- -- - |


# # Closure
| - -- Closure - -- |
| ・ Data |
| ・ Data manipulation functions |
| - -- -- -- -- -- - |

Correction of Dan Kogai

  • perl - Class vs. Closure (404 Blog Not Found)
  • Closures can do the same thing as classes, but in Perl creating closures costs more than creating objects.
  • I wrote that closures cannot be inherited, but it seems that they can be inherited. (I can't think of a way to do it for now.)

Related Informatrion