converting libraries into modules
Andre_br
created: 2006-03-04 21:08:16
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

Re: converting libraries into modules
created: 2006-03-04 21:30:23
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 nothing 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?
Re^2: converting libraries into modules
created: 2006-03-05 13:00:10
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

Re^3: converting libraries into modules
created: 2006-03-05 13:47:00
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^4: converting libraries into modules
created: 2006-03-06 03:39:17
I agree, although I do not know what ...solves the OP's problem to a 't'. means :):)
Re: converting libraries into modules
created: 2006-03-05 05:26:57
You're almost there. Here's how to define the object:
package MyModule;

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 = {};
    bless $self, $class;
    return $self;
}

sub half {
    # if you call $m->half(10), $_[0] will be the object
    my ( $self, $number ) = @_;
    return $number / 2;
}

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)), 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).

Hope this helps. Good luck!
Re: converting libraries into modules
created: 2006-03-05 05:44:07
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
created: 2006-03-05 06:08:42
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
Re^2: converting libraries into modules
created: 2006-03-05 13:16:29
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é
Re^3: converting libraries into modules
created: 2006-03-05 13:34:38
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, so, 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!
Re^2: converting libraries into modules
created: 2006-03-05 16:27:54

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.

Re^3: converting libraries into modules
created: 2006-03-05 17:34:16
chromatic,
While I tend to agree that having single functions that work as methods and exportable functions 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

Re^4: converting libraries into modules
created: 2006-03-05 19:08:49

It could be as simple as:

use vars '@EXPORT';

@EXPORT = qw( list of methods to curry );

sub import
{
    my $class          = shift;
    my $curried_object = $class->new();
    my $caller         = caller();

    for my $export ( @EXPORT )
    {
        *{ $caller . "::$export" } = sub { $curried_object->$export( @_ ) };
    }
}
Re^5: converting libraries into modules
created: 2006-03-07 08:12:18
I like this one, it's very clever and it will even work with InsideOut or closure objects, that's great :)
Re^2: converting libraries into modules
created: 2006-03-05 17:43:20
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.
Re^3: converting libraries into modules
created: 2006-03-06 11:05:08
# uncuddles elses are faster!

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


Re^4: converting libraries into modules
created: 2006-03-06 23:28:21
No, really, have you tried it with Benchmark?

perlmonks.org content © perlmonks.org and Andre_br, blahblah, chromatic, Cody Pendant, Courage, dragonchild, Limbic~Region, rvosa, wazoox

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03