Search code examples
perloopreusabilitycode-reuse

How to add a user-defined function as a built-in function in Perl?


At one time, I was thinking about how I could create a package (or class) in Perl in order for code re-usability. Coming from a C++ background, I had trouble understanding how Perl subroutines and packages work. So, I created a Stack data structure in Perl (which is actually just an array with a LIFO approach) which needed me to write down the loop every time I want to print it out (which is very inefficient)

@_ = (1, 8, 14, 45);

push(@_, 69);
push(@_, 55);
push(@_, 65536);
push(@_, 4294967296);

print "Print without newline: ";

$size = @_;
for my $i(1 .. ($size-1))
{
    print $_[$i]." ";
}

print "Print with newline: ";

for my $i(1 .. ($size-1))
{
    say scalar $_[$i];
}

pop(@_);
pop(@_);
pop(@_);

print "Print without newline: ";
# HERE WE GO AGAIN
$size = @_;
for my $i(1 .. ($size-1))
{
    print $_[$i]." ";
}

Then, I started to learn more about creating packages in order to reuse codes. Here's one example where I did it.

Printer.pm

use Modern::Perl;

package Printer;
sub new
{
    my $class = shift;
    my $self = {};

    bless $self, $class;

    return $self;
}

my $size;
sub println
{   
    my @userArray = @_;
    $size = @userArray;
    for my $i(1 .. ($size-1))
    {
        say scalar $userArray[$i];
    }
}

sub print
{   
    my @userArray = @_;
    $size = @userArray;
    for my $i(1 .. ($size-1))
    {
        print $userArray[$i]." ";
    }

}

1;

Pseudocode for Stack.pl

use lib ".\\";
use Array::Printer;

. . .

my $printer = new Printer();
print "Print without newline: ";
print $printer->print(@_);
print "\nPrint with newline: \n";
print $printer->println(@_);

say "";

# POPPING (LIFO)
pop(@_);
pop(@_);
pop(@_);

print $printer->print(@_);

And, as you've guessed it, it works like charm.

One day though, one thing popped into my mind. I wondered if I could make the 'println' function that I built in the Printer package as a 'built-in' function. No calls are made the the package's constructor. I'm thinking of something like this:

println(@_);

Since println doesn't exist in Perl, I'm just curious if it's possible to do so. Any suggestions on what should I do next?


Solution

  • If you want to emulate a built-in, then you don't need a class, you just need a library. Libraries are also implemented as packages in Perl, but the subroutines in them don't expect to have a classname or object passed as their first parameter. You can also use Exporter to insert your subroutines into the calling program's symbol table.

    It would look something like this:

    package Array::Printer;
    
    use strict;
    use warnings;
    use feature 'say';
    
    require Exporter;
    our @ISA = qw[Exporter];
    our @EXPORT = qw[println];
    
    sub println {
      say for @_;
    }
    
    1;
    

    And you would use it like this:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use lib '.';
    use Array::Printer;
    
    # I've used @stack instead of @_ in my code as @_ is a
    # special variable and it seems strange to use it in another way.
    
    my @stack = (1, 8, 14, 45);
    push(@stack, 69);
    push(@stack, 55);
    push(@stack, 65536);
    push(@stack, 4294967296);
    
    println(@stack);
    
    pop(@stack);
    pop(@stack);
    pop(@stack);
    
    println(@stack);
    

    Update: Notice that my library module is called Array::Printer. Your original class should have used the same name. Generally, Perl package names (both libraries and classes) should match the name of the file they are stored in. You called your package Printer but you used Array::Printer. This is guaranteed to confuse any maintenance programmer (even you!)

    The use lib '.' in my code adds the current directory to the list of directories that Perl searches for libraries and classes. So we're assuming that the code for Array::Printer is in a file called Printer.pm in a directory called Array in your current directory (which is also, presumably, where your main program is. A double-colon in a Perl library name (::) is translated to a directory separator (/) when looking for the file that contains the library.