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

Monks, in my error messages, I strive to tell the users of my modules which argument has got something wrong with it and why. When I can't get perl to give me the error I want, I get mean. Can someone help me recover my happy feelings?
use strict; use warnings; wants_foo_object({}); sub wants_foo_object { die "no object" unless defined(my $obj = shift); die "not an object" unless ref($obj); die "not a foo" unless $obj->isa('foo'); #the error I want } #The error I get: #C:\thomasdata\experiments>perl unblessedWantBetterError.t #Can't call method "isa" on unblessed reference at unblessedWantBetter +Error.t lin #e 10.

Edit g0n - retitled from "sub Wants_foo_object() wants a better error message (how to differentiate hash reference and object reference)"

Replies are listed 'Best First'.
Re: How to differentiate hash reference and object reference?
by jdhedden (Deacon) on Aug 31, 2005 at 11:59 UTC
    Use blessed in Scalar::Util instead of testing for ref:
    use strict; use warnings; use Scalar::Util 'blessed'; wants_foo_object({}); sub wants_foo_object { die "no object" unless defined(my $obj = shift); die "not an object" unless blessed($obj); die "not a foo" unless $obj->isa('foo'); }

    Remember: There's always one more bug.
Re: How to differentiate hash reference and object reference?
by tlm (Prior) on Aug 31, 2005 at 11:59 UTC

    Check out Scalar::Util::blessed. You can also use UNIVERSAL::isa( $obj, 'foo' ).

    the lowliest monk

      Be very careful calling UNIVERSAL::isa as a function, though, and be sure that's what you want. It's nice in that you can throw any reference at it whether or not it's blessed, but it doesn't allow various facade or delegation patterns that don't rely on @ISA inheritance, but do override isa to signal that they do, indeed, subclass an object through other means. This is probably better (using blessed as recommended):

      blessed $obj && $obj->isa('foo')

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: How to differentiate hash reference and object reference?
by Arunbear (Prior) on Aug 31, 2005 at 13:10 UTC
    Are you doing this in a test suite as the .t suggests? Then use Test::More:
    use warnings; use strict; use Test::More qw(no_plan); my $foo = {}; isa_ok($foo, 'My::Object');
    which gives:
    not ok 1 - The object isa My::Object # Failed test (temp.pl at line 6) # The object isn't a 'My::Object' it's a 'HASH' 1..1 # Looks like you failed 1 test of 1.
      No, I'm thinking more, help users of the module debug fast and obviously. Good tip though.
Re: How to differentiate hash reference and object reference?
by demerphq (Chancellor) on Aug 31, 2005 at 13:19 UTC

    I have to say that I wonder about validation code like this. Whats the point? You are checking the type to throw an error, but what will happen if you try to use the object without doing these checks? Remarkably, in almost every way the exact same thing will happen (under strict anyway), ie, the interpreter will throw an errror.

    So just let the interpreter throw the error.

    Now I can hear the peanut gallery saying "yeah but what happens if they pass in an object that is blessed into a package that has the methods im concerned about?" My answer would be, you mean you know in advance that this would be wrong? What happens if they decide to reimplement the interface in a different class structure and decide to use that?

    IE, most of this type of validation just removes the flexibility of the consumer, and adds very little or nothing to the robustness of your code.

    I will agree there are cases where doing checks like these properly is vital, but IMO that is usally confined to cases where you have specific handlers for different input types, not simply screen for a valid type.

    Ultimately if you _do_ want to do checks like these then you should look into using Scalar::Util.

    ---
    $world=~s/war/peace/g

      The thing is, I want to be able to control my error messages. Right now, I have
      #The error I get: #C:\thomasdata\experiments>perl unblessedWantBetterError.t #Can't call method "isa" on unblessed reference at unblessedWantBetter +Error.t lin #e 10.
      It is an error message, but it comes from the compiler, not from me. My main problem with it is isn't so much the wording, but the fact that it doesn't even say which argument caused the problem. Maybe params validate solves that?

      That said, I am overall not currently happy with how I'm going about params checking. I will have a look at Params::Validate like diotalevi said, and maybe give my whole way of doing this a rethink.

        My main problem with it is isn't so much the wording, but the fact that it doesn't even say which argument caused the problem.

        Sure it does. the message says

        Can't call method "isa" on unblessed reference at unblessedWantBetterError.t line 10.

        Which tells us that on line 10 some $var is being used as an object in a method call and the content of $var is not actually an object. When we look at line 10 we see

        die "not a foo" unless $obj->isa('foo'); #the error I want

        which tells us that the $var in question is called $obj.

        Now presumably you are doing this because later on you have code that does something like

        $obj->do_something($useful);

        But what do you gain? In this situation you would see the same error you are now just on the actual line where the actual code is being used, and not on some validation code.

        So IMO you gain nothing but code complexity and you tie the hands of your codes consumers because they can't drop in a different object with a compatible interface. So IOW, you have made your code less usable and robust than more.

        ---
        $world=~s/war/peace/g

Re: How to differentiate hash reference and object reference?
by davis (Vicar) on Aug 31, 2005 at 12:02 UTC
    Check out the ref function: (see perldoc -f ref)
    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my $foo = {}; bless($foo, "SOMECLASS"); #Comment this line out. my $type = ref $foo; print $type, "\n";

    davis
    Kids, you tried your hardest, and you failed miserably. The lesson is: Never try.

      No, don't do that. ref is broken:

      • It fails for certain valid invocants (class names).
      • It prohibits polymorphism, both in inheritance and interface equivalence.
      • It returns undef on failure, so you have to go through contortions to avoid undefined value warnings.
      • It's trivially easy to fool by blessing into a package named HASH.

      perlfunc in bleadperl recommends several other techniques for doing the same thing without the problems.

        You can also fool ref() by blessing your objects into the "\0" package. In that case, you'd get a false value on something that is not only a ref but is even blessed.
        Oh, didn't know that. Thanks!

        davis
        Kids, you tried your hardest, and you failed miserably. The lesson is: Never try.
Re: How to differentiate hash reference and object reference?
by holli (Abbot) on Aug 31, 2005 at 12:44 UTC
    From perlfunc:
    ref EXPR
    ref


    Returns a true value if EXPR is a reference, false otherwise. If EXPR is not specified, $_ will be used. The value returned depends on the type of thing the reference is a reference to. Builtin types include:

    SCALAR
    ARRAY
    HASH
    CODE
    REF
    GLOB
    LVALUE

    If the referenced object has been blessed into a package, then that package name is returned instead. You can think of ref as a typeof operator.
    So, there's no need to call ->isa():
    sub wants_foo_object { my $obj = shift; die unless $obj && ref($obj) eq "foo"; #... }
    (untested)


    holli, /regexed monk/
      But ->isa return's true if the argument is in the package's @ISA array. ref( $obj ) does something different. I think UNIVERSAL may be more the way to go.

      Ordinary morality is for ordinary people. -- Aleister Crowley

        No, isa() returns true if whatever method isa() is for the invocant it returns true. If I wrote an object and overrode isa() to return true based on a the phase of the moon, that's what it would do.

        That's probably unwise code to write, but the purpose of having isa() be a method is so that classes can override it when it makes sense to do so.

Re: How to differentiate hash reference and object reference?
by diotalevi (Canon) on Aug 31, 2005 at 12:48 UTC

    For fucks sake, just use Params::Validate!

    sub wants_foo_object { my ( $foo ) = validate_pos( @_, { class => 'foo' } ); .... }

      Ordinarily, I'd have voted ++ for suggesting Params::Validate, but -- for unnecessary profanity. That's not what I want to see on this site.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        That's not what I want to see on this site.
        And it's absolutely not what we are used to from diotalevi. I wonder what has bitten him.


        holli, /regexed monk/