Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to not export all functions/methods from a package in perl?

Tags:

object

perl

I am playing around with a existing perl module lets call it Obj. I have added some new features (subroutines / methods) to Obj but store them in another .pm call it Foo. However I dont want Obj to inherit every sub from Foo.

Now I have been reading perl documentation for a few hours and am confused. https://perldoc.perl.org/Exporter#Selecting-What-to-Export Just says 'Do not export method names!'

Here is some example code, I'd like to not see sub _not_exported from Obj.pm:

#!/usr/bin/env perl
use strict;
use warnings;
use diagnostics;

package Foo;# Foo is a new PM file used to extend Obj with collections of subroutine's.
# I want to use hello() from Obj
    #use Exporter 'import';
    use Exporter qw(import);
    our @EXPORT_OK = qw/hello/;

    sub hello {
            my $self = shift;
            $self->{test} = 'Hello world';
    }
    # I'd rather not pollute Obj with this sub
    # or perfreably all subs the begin with a underscore
    # How do I exclude this sub?
    sub _not_exported { 
        my $self = shift;
        return 'Exported';
    }


    #main old code object module 
package Obj; # large old PM file I dont want to change this file much
    # pull in the new module
    use base 'Foo'; 
    # use base seems better than this:
    #import Foo;
    #our @ISA = qw(Foo);
    sub new {
        
            my $self = {};
            bless $self, 'Obj';
            return $self;
    }
    eval { $this = $form->_not_exported() } ; #Should return nothing
    
    sub catch_hash {
        my ($self,$arg) = @_; 
        $arg |= '';
        $self->{test} = 'catch';
    }

    
#Perl script, creates an object using Obj;
package main;
    #use Obj;
    import Obj;

    my $form = Obj->new();
    $form->catch_hash();
    print "Toss? '$form->{test}' \n";
    $form->hello();
    print "\nHello? '$form->{test}'\n";
    my $this = "";
    eval { $this = $form->_not_exported() } ; #Should return nothing
    print "$this\ndone\n";

    
1;

I am open to other options like Moo / Moose but dont want to change the old code much. Thanks in advance

like image 360
turtle Avatar asked Dec 29 '25 13:12

turtle


1 Answers

[Note: I'm am a former maintainer of Exporter]

I believe you've confused exporting with inheritance. That's easy to do, Perl doesn't draw a line between "function" and "method", they're just sub.

tl;dr You don't need to export, that's just how inheritance works, there is a work around.


Exporting lets you call a function from outside of a package without fully qualifying it. It would let you call Foo::hello as just hello. Exporting lets Perl know that hello really means hello in package Foo.

But these are method calls, and you call them on a class or object. my $foo = Foo->new; $foo->hello. No exporting required. Foo->new calls new in Foo and returns a Foo object. $foo->hello knows to look for the method foo in the ancestry of $foo's class. You don't need to use exporter in a class, that's what "Do not export method names" means.

Exporting is a deliberate act which copies symbols around. Inheritance is all or nothing. If you inherit from a class you get all its methods (subs). This is a consequence of inheritance, and there are many other alternatives to inheritance such as composition.

In other OO languages you could declare the method private and it would not be inherited. Perl doesn't have that. Normally you just live with this by convention as you have, put an underscore in front of the method name, don't document it, and if somebody uses it that's their problem. And that's usually fine.

But you can make truly private methods with anonymous subs and lexical variables.

package Foo;

# Define `new` in Foo. Obj will inherit it.
# Do not hard code the class.
# `new` receives the class new was called on.
sub new {
    my $class = shift;
    return bless {}, $class;
}

sub hello {
    my $self = shift;
    $self->{test} = 'Hello world';
}

# This is a reference to an anonymous function in a lexical variable.
# It can only be seen by the code after this line in this file.
my $private_method = sub { 
    my $self = shift;
    return "private method called by $self with args: @_\n";
};

sub public_method {
    my $self = shift;

    # $private_method can be seen here.
    # A subroutine reference can be called like a method.
    print $self->$private_method("basset hounds got long ears");
}

And in Obj.pm

package Obj;

# parent is a lightweight replacement for base.
use parent 'Foo'; 

Obj inherits new and hello and public_method from Foo, but it cannot see $private_method. It can only be seen inside Foo.pm.

my $obj = Obj->new;

# private method called by Obj=HASH(0x7fcfdb8126d8) with args: basset hounds got long ears
$obj->public_method;

Because public_method is defined where it can see $private_method, $obj->public_method will work despite being called on an instance of Obj and not Foo.

like image 192
Schwern Avatar answered Jan 01 '26 06:01

Schwern



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!