Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

how to export a method

by toothedsword (Sexton)
on Oct 22, 2019 at 02:18 UTC ( [id://11107807]=perlquestion: print w/replies, xml ) Need Help??

toothedsword has asked for the wisdom of the Perl Monks concerning the following question:

I have written a CV.pm(perl5/5.30.0/lib/site_perl/5.30.0/x86_64-linux/PDL/CV.pm) as below:

package PDL::CV; use strict; our @EXPORT=qw(rp); sub rp { my ( $self, $input ) = @_; $self .= $input->reshape($self->dims); return $self; } sub new { my $class = shift; my $self = { _input => shift, }; bless $self, $class; return $self; } 1;
Then I wrote a test.pl as below:
use v5.10; use PDL; use PDL::NiceSlice; use PDL::CV; $d = sequence(4,5); $t = pdl sequence(100,5,5); $d(0,1:4)->rp($t(0,0,1:4));

When I ran this scripts, I get this message: Can't locate object method "rp" via package "PDL" at test_pdl6.pl line 28. If I do not want using $d(0,1:4)->PDL::CV::rp($t(0,0,1:4)); just using $d(0,1:4)->rp($t(0,0,1:4)); How should I do?

Replies are listed 'Best First'.
Re: how to export a method
by choroba (Cardinal) on Oct 22, 2019 at 06:19 UTC
    PDL doesn't behave as a normal Perl object oriented module. Its quirks are described in PDL::Objects which also shows how to subclass it, which is exactly what you need.
    package PDL::CV; use parent qw{ PDL }; # Tell Perl what your parent class is. sub rp { my ($self, $input) = @_; $self .= $input->reshape($self->dims); return $self } sub new { my $class = shift; return bless { PDL => shift, # See PDL::Objects for details. }, $class }

    You also need to instantiate the new class:

    my $d = PDL::CV->new(sequence(4,5)); my $t = pdl sequence(100,5,5); $d(0,1:4)->rp($t(0,0,1:4));

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      The PDL idiom was put together in the late 90s, and thanks to backward compatibility it will be difficult or impossible to change.

      That idiom can be thought of as the various modules (PDL::Primitive, PDL::Ops) being roles, and they add "methods" to the PDL namespace. The answer to the question here, in order to operate like a typical PDL module, would both export a rp function, and implement a PDL::rp function that would add a PDL-object method. Because this is a function that only makes sense with an ndarray as its input, the natural thing would be to alias the exportable function to the PDL:: version:

      package PDL::CV; use strict; use warnings; use Exporter 'import'; our @EXPORT_OK = qw(rp); *rp = \&PDL::rp; # alternative idiom, e.g. for zeroes: sub zeroes {PDL->zeroes(@_)} sub PDL::rp { my ($self, $input) = @_; $self .= $input->reshape($self->dims); $self; }
      It is very important in this specific instance to note that reshape (as seen in PDL::Core#reshape) "changes the ndarray in place", i.e. is a mutation. PDL needs a new withdims affine transformation which would do what the OP thinks reshape does. EDIT: See https://github.com/PDLPorters/pdl/issues/217 for discussion of reshape and its problems.
Re: how to export a method
by wanna_code_perl (Friar) on Oct 22, 2019 at 02:48 UTC

    You'll need to require Exporter; and inherit before your @EXPORT will have any effect.

    See Exporter for examples, but usually it goes something like this:

    require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(rp);

    Also, perhaps you've already thought it through, but if not, I'd suggest carefully considering whether you want to export a function called rp into the caller's namespace by default. Consider using @EXPORT_OK instead.

      You'll need to require Exporter;

      Wrong. You can load the Exporter modue at compile time, no need to delay until runtime, i.e. use Exporter instead of require Exporter.

      and inherit

      Wrong for any perl released since at least 2008. Inheriting from Exporter has side effects that may be unwanted. In particular, it will make Exporter's methods and function appear as methods of the class inheriting from Exporter. Prepare for really strange bugs if the class unintentionally inheriting from Exporter also inherits from another class that has methods with the same name as those inherited from Exporter.

      Generally, importing the import() method from Exporer is sufficient and has no unwanted side effects.

      See also Exporter in an OO module?, Advice on style.

      before your @EXPORT will have any effect.

      Wrong. OOP modules should not export methods, as others have explained.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Wrong. ...
        Wrong. ...
        Wrong. ...
        there are no sweeter words than "I told you so". ;-)

        Yeah, I'm starting to see that about you :-)

        Points well taken about require vs. use, and @ISA. The very first example in the Synopsis has long done what I need it to do. Maybe it's time for a documentation patch to de-emphasize the older calling convention? No reason for it to have top billing if it's not recommended for new code. Anyway, I'll definitely be adopting this advice going forward. Thanks.

        OOP modules should not export methods, as others have explained.

        Yes, I was the first to explain that, some hours before your reply.

        Thank you for you help. I will try it.
      Thank you very much for your reply! But when I added the require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(rp); to CV.pm like this:
      package PDL::CV; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(rp); sub rp { my ( $self, $input ) = @_; $self .= $input->reshape($self->dims); return $self; } sub new { my $class = shift; my $self = { _input => shift, }; bless $self, $class; return $self; } 1;
      I also got the message: Can't locate object method "rp" via package "PDL" at test_pdl6.pl line 27.

        Ah, I completely missed the fact that you're mixing Exporter with OO programming. My mistake for not spotting that sooner. But the error message you're getting has nothing with Exporter, because object methods don't need to be (and should not be) exported, because there's no reason to pollute the caller's namespace when object methods can be called regardless. The real problem is evident in the error message you get:

        Can't locate object method "rp" via package "PDL" at test_pdl6.pl line 27.

        Is for the class "PDL", rather than your "PDL::CV" package where the rp method is defined. The problem must lie with your test (caller) code in test_pdl6.pl, because aside from the needless use of Exporter, your package's code looks reasonable.

        This should have been my top level reply.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11107807]
Approved by wanna_code_perl
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2024-04-24 04:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found