Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

converting libraries into modules

by Andre_br (Pilgrim)
on Mar 05, 2006 at 02:08 UTC ( [id://534548]=perlquestion: print w/replies, xml ) Need Help??

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

Hello my esteemed fellow monks

Iīve been coding Perl for a couple years already, but now I feel itīs time to convert my Perl4-style custom libraries into real .pm modules. The main reason is because Iīm implementing mod_perl and it seems Apache::Reload is only guaranteed to track updates on module-like 'libraries', called with use.

Iīve been through the manpages and also the Camel book, but the OO literature just feeks like Greek to me. Just canīt figure out how to do it! I get lost in the therminology real fast. So I wonder if you guys can give me and applied explanation, say, about how would be a module created after library that looks like this:

[mylibrary1.pl] sub half { my $number = shift; return $number / 2; }
The main issues are:
1) Ok, I know I have to save the file as .pm and declare first thing the name of the package  package Mypackage;. This one is clear for me, ok.
2) Then I have to provide the list of functions I want to export, right?
require Exporter; @ISA = qw(Exporter); @EXPORT = qw(half);

Ok, no problem with this one either.
3) Now is where I start to get lost. I don't know how to handle the new thing. I mean, there is something that shall be there, in order I can in the main say  my $m = new Mymodule; in the main, right? I tried  sub new {return bless {}; } but this feels so strange.
4) Then, with all set up, how shall I call the functions from the main?  $m->half(10); ? This doesnīt work, since the output is Mymodule=HASH(0xbd5068) and not just 5! What am I doing wrong? Do I have to modify something in the half() function (and all of them), now thatīs a module-like library??

I really thank you if you can help me out on this. Sorry if this is too OO-newbie, but I think it may be an interessting material for the community, as I couldnīt find any didatic explanation on this library-module conversion over the web.

Thanks a lot!

Andre

Replies are listed 'Best First'.
Re: converting libraries into modules
by dragonchild (Archbishop) on Mar 05, 2006 at 02:30 UTC
    In Perl5, libraries and classes are implemented using the same mechanism (packages, usually in their own .pm files), but they're completely different. If you're converting P4-style libraries to P5-style, all you need to do is follow this template.

    Old:

    [mylibrary1.pl] sub half { my $number = shift; return $number / 2; }

    New:

    [mylibrary1.pm] package mylibrary1; use base 'Exporter'; use vars qw( @EXPORT @EXPORT_OK ); @EXPORT = qw( half ); # These are the ones that are imported if nothin +g is specified. @EXPORT_OK = qw( half ); # These are the ones you may specify, if you +want. sub half { my $number = shift; return $number / 2; } 1; # All .pm files must end with a true value __END__

    Now, when you use them, you'll do it like this:

    Old way:

    [myscript.pl] require 'mylibrary1.pl'; print half( 10 ), "\n";

    New way:

    [myscript.pl] use mylibrary1 qw( half ); print half( 10 ), "\n";

    Now, the different between @EXPORT and @EXPORT_OK is how you construct the 'use' line. Here's a few examples:

    use Foo; # Imports everything in @EXPORT, nothing in @EXPORT_OK use Foo 'bar'; # Imports 'bar' if it's in @EXPORT_OK. Imports nothing +from @EXPORT use Foo (); # Imports NOTHING from either.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      although using Exporter is suggested by, say, h2xs, I think using Exporter isn't a good habbit.
      Its not good to pollute namespace.

      Using package::utilname(...); $utlobject->utl_method(...); is flexible enough and no real need to save few keystrokes by not pointing package name, and risking to have naming conflict later....

      Also, extra dependency on Exporter, and many extra lines in your template to introduce Exporter, do not worth the effort.

      Best regards,
      Courage, the Cowardly Dog

        While I agree with you from a purity perspective, Exporter exists specifically to make the transition between P4 repositories and P5 libraries easier. In other words, Exporter is the exact right tool in this situation.

        Now, if the OP wants, we can educate him on better software practices later. But, for now, this solves the OP's problem to a 't'.


        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: converting libraries into modules
by rvosa (Curate) on Mar 05, 2006 at 10:26 UTC
    You're almost there. Here's how to define the object and methods:
    package MyModule; # 'constructor' sub new { # if you call MyModule->new, $_[0] will be 'MyModule' my $class = shift; # $self can be any reference, but usually a hash my $self = {}; # this is the 'magic' that creates the object bless $self, $class; return $self; } sub half { # if you call $m->half(10), $_[0] will be the object my ( $self, $number ) = @_; # proceed as normal return $number / 2; } package main; # now no longer in package, but in script # instantiate object, call constructor my $m = MyModule->new; # yields '5' print $m->half(10);
    What happened in your case was that you tried to divide the first argument in @_ in your 'half' sub by two. However, if you use 'half' as a method call (i.e. $m->half(10), instead of half(10)), the first argument is the $m object (sometimes called the 'invocant'), not the number 10.

    Hence, the return value (being whatever you pulled off $_[0]) is the object, which, when printed out, yields Package=REFTYPE(address), in your case Mymodule=HASH(0xbd5068). Instead, you will want to divide the second argument, which is why, conventionally, people will copy from @_ like so: my ( $self, ... ) = @_;

    Hope this helps. Good luck!

    P.s. you don't need to use EXPORTER for OO modules, as the methods are attached to the objects (rather than having to be imported into the caller's namespace).
Re: converting libraries into modules
by Cody Pendant (Prior) on Mar 05, 2006 at 10:44 UTC
    how shall I call the functions from the main? $m->half(10); ? This doesnīt work, since the output is Mymodule=HASH(0xbd5068) and not just 5! What am I doing wrong?

    If you're doing it the $m->half() way, then you need to have a module that looks like this:

    package MyPackage; sub new { my ($class) = shift(); my $self = {}; return bless $self, $class; } sub half { my $self = shift(); my $number = shift(); return $number / 2; } 1;
    and a script that looks like this:
    use MyPackage; my $m = MyPackage->new(); print $m->half(10);
    Where you create a new MyPackage object and then use the object->function call to get the result.


    ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
    =~y~b-v~a-z~s; print
Re: converting libraries into modules
by Cody Pendant (Prior) on Mar 05, 2006 at 11:08 UTC
    You can also write your functions so they work either way.

    Say you create your module and export the half() function as before, you can have your function look like this:

    sub half { my $number; if (ref($_[0]) eq 'MyPackage') { my $self = shift(); $number = shift(); } else { $number = shift(); } return $number / 2; }
    so that, if called via $m->half(), it works the first way, but if called just via half() it works the second way.


    ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
    =~y~b-v~a-z~s; print

      Ugh, please don't do that. It's brittle to maintain, hard to document, confusing to explain, and I've never seen a case where it really simplifies things.

      If you really need to export a simple procedural interface, create an object and export curried functions that use the object to call the methods.

        chromatic,
        While I tend to agree that having single functions that works as a method and an exportable function is usually a bad idea, your solution seems a bit over the OP's head. Would you mind providing a code sample, point to an existing wheel, or link to some documentation that does a better job explaining.

        For what it is worth - I really like that Perl6 will distinguish between methods and subs as well as provide multi-method dispatch.

        Cheers - L~R

      Great! Now I got it!
      Thanks a lot for the replies, dragonchild, Courage, rvosa and Cody Pendant (great tip of yours, cody, this calling-aware approach!).
      Iīm so glad to have you guys around, fellow monks!
      Take care!
      André
        Just to document an adaptation on Cody's approach, that Iīve just tested with success:

        Instead of

        sub half { my $number; if (ref($_[0]) eq 'MyPackage') { my $self = shift(); $number = shift(); } else { $number = shift(); } return $number / 2; }
        We can make the adaptation of all perl4-ish functions possible just by adding a single line, on the top of each function's code:

        sub half { if (ref($_[0]) =~ /MyPackage/ ) { my $self = shift; } # as the former Perl4-like functions donīt need the $self object, this + line's role is to clean up the first element of the @_ array! And, s +o, make things ready for the shifts your code already is set to! my $number = shift; return $number / 2; }
        Detail: I call all my methods libraries MyPackage, Mypackage2 etc, so by testing with this regexp, I don't even have to worry to adapt this one-line according to the library Iīm converting into module! Just adding it all over the subs, and finally enter the OO world!
      I don't think this approach really simplifies things (now you have two interfaces to explain), but if you do it this way I'd suggest you don't hardcode the class name into its methods. I think I'd do:
      sub half { my $number; if (ref $_[0] eq __PACKAGE__) { my ( $self, $number ) = @_; } # uncuddles elses are faster! else { $number = $_[0]; } return $number / 2; }
      Or even:
      sub half { my $number; if (ref $_[0]) { my ( $self, $number ) = @_; } # uncuddles elses are faster! else { $number = $_[0]; } return $number / 2; }
      And then, if you're one of those suspenders + belt (just to be safe, you know) kind of people, you can always do looks_like_number $number and throw an exception (with baroque stack trace) on false.
        # uncuddles elses are faster!

        What? Is that really true? I thought it was just a style choice...


Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (None)
    As of 2024-04-25 00:10 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found