13

Puns aside, I'm trying to implement an import method in my Perl class to instantiate a Data object, which is essentially a glorified array of hashrefs, from a proper array of hashrefs.

Here's an example of how I plan to use it:

# Pull in the data
my $data = Data->import(
                         [
                           { a => 1, b => 7, c => 3},
                           { a => 7, b => 9, c => 2},
                         ]
                       );

$data->manipulate;  # Use package methods

My import implementation is as follows:

package Data;

sub initialize {
    my $class = shift;
    my $data = [];
    bless $data, $class;
    return $data;
}

sub import {

    my ( $class, $data ) = @_;

    bless $data, $class;
    return $data;
}

1;

The surprising thing is that Perl reports the error at compile-time (note the BEGIN block):

Can't bless non-reference value at Data.pm line 51.
BEGIN failed--compilation aborted at myScript.pl line 8.

perldiag didn't add much clarity to what's going on:

Can't bless non-reference value

(F) Only hard references may be blessed. This is how Perl "enforces" encapsulation of objects. See perlobj.


I even tried initializing the object and adding the data in two separate steps:

sub import {                         #< Another constructor >

    my ( $class, $data ) = @_;

    my $obj = $class->initialize;
    push @$obj, @$data;

    return $obj;
}

This resulted in the following compile-time error:

Can't use an undefined value as an ARRAY reference...
BEGIN failed--compilation aborted at...

Two questions:

  1. What's wrong with what I've done?
  2. Could someone please clarify the perldiag explanation of this compile-time error?
Zaid
  • 36,680
  • 16
  • 86
  • 155
  • Hindsight : Is there any better reference than `perldiag` to understand warning/error messages? – Zaid Sep 11 '11 at 10:30
  • 2
    In general, you can attack errors like this by inserting a print of your preconditions. In this case, the arguments to your subroutine. (`print "import got: @_\n";`) That would have let you know that what you thought was an array actually wasn't. Next, add a guard, something like `ref $data eq 'ARRAY' or confess "not an array: '$data'";` and you will find out where the offending call is coming from. (`confess` is from `use Carp 'confess';` and provides a full backtrace) Keep that line in, as it may help you immediately catch other errors in the future, perhaps changing `confess` to `croak`. – Eric Strom Sep 11 '11 at 20:53
  • @Lance Roberts : The original title was a play on words in reference to the [`Acme::Damn`](http://search.cpan.org/perldoc?Acme::Damn) module (which does the opposite of `bless`) – Zaid Nov 14 '11 at 14:32

1 Answers1

21

import() is a dangerous name to give a method, as Perl sometimes calls a method called 'import' for you. In fact, the statement:

use Data;

really means:

BEGIN {
    require Data;
    Data->import();
}

which happens during 'compilation'.

So it's likely that your import() method is being called by Perl as part of the class loading mechanism before you get a chance to use it. You should rename your import() method to something else (e.g. import_data) and you should be fine.

Alex
  • 5,863
  • 2
  • 29
  • 46
  • If that were the case, he wouldn't be getting the error he's getting. – Brian Roach Sep 11 '11 at 10:07
  • 3
    Yes, he would. In both cases Perl would call Data->import(), so $data never exists, causing the 'no object to bless' error, and the 'no array to dereference' error. – Alex Sep 11 '11 at 10:09
  • +1 : This was the issue, renamed `import` to `load`. My sanity has been restored. Thanks! – Zaid Sep 11 '11 at 10:24
  • 9
    I have always thought that the `import` method should be the `IMPORT` method to go along with `DESTROY` and other language blocks like `BEGIN`. The user/programmer doesn't expect that an all lower case declaration of a method would a) have a different meaning than any other and b) cause this problem. – Joel Berger Sep 11 '11 at 20:02