in reply to Re: RFC: DBIx::Iterator
in thread RFC: DBIx::Iterator

Why UNIVERSAL:­:isa? Do you expect people to pass in blessed references? Do you want to forbid tied variables?

You confuse me. Some of the advantages of isa are that it doesn't forbid blessed references and it doesn't forbid tied variables.

One of the problems with using isa() as a function instead of as a method is that items that pretend to be some type of data structure via overloading are not detected by direct use of UNIVERSAL::isa() alone. One way to help isa() is for such classes to implement their own isa() method. Unfortunately, that work-around is often quite inconvenient for code like above to make use of (because $ref->isa("HASH") is most likely to just die, even though it works better for this one case).

A much better (IMHO) work around would be for such an overloading class to do push @ISA, "HASH"; so that UNIVERSAL::isa( $ref, "HASH" ) would be true.

Doing a tiny bit of testing, I see that UNIVERSAL::isa() is somewhat pickier than I assumed and to make this work one must also do something to create the "HASH" package, for example, { my $no_op= @HASH::ISA; }.

So I consider UNIVERSAL::isa() to be a "best practice" because it gets (as near as I can tell) all but the one rarest case right, it is very simple and easy to understand (doing *isa= \&UNIVERSAL::isa; makes it even nicer), and that one rarest case is nearly trivial to make work as well.

A patch to overload.pm do this push @ISA, "HASH" and create the HASH package (etc.) when appropriate would be well worthwhile, IMHO. I apologize that I won't be attempting such a patch any time soon for a number of reasons.

I hope I got that all correct. :)

Update: And let me address one potential complaint that I've seen before. One other aspect of UNIVERSAL::isa() is that you can use the technique I gave above to lie, intentionally making UNIVERSAL::isa() incorrectly return a true value for your objects. This aspect does not bother me because "Doctor, it hurts when I do this", that is, if you want to go out of your way to intentionally break my simple code, I just don't care. More importantly, such an ability can prove extremely useful for some of those edge cases you run into when writing unit tests and trying to get full code coverage. So I consider this aspect to be a feature. Note that you can also (probably) cheat in the other direction as simply as doing *UNIVERSAL::isa= sub { ... };.

I can certainly see the point in trying to prevent people from accidentally creating a class that breaks my simple code. I think it folly to try to prevent people from intentionally trying to break such code.

So it'd be really cool if overload.pm were "fixed" to co-operate with this technique. If I saw some other candidate for "best practice" here that was as close to "right" and as available yet didn't require this somewhat hackish trick, then I'd be pushing it instead. All of the alternatives have much worse draw-backs, I believe.

- tye        

Replies are listed 'Best First'.
Re^3: RFC: DBIx::Iterator (isa)
by chromatic (Archbishop) on May 12, 2007 at 06:16 UTC
    So I consider UNIVERSAL::isa() to be a "best practice" because it gets (as near as I can tell) all but the one rarest case right...

    Unfortunately, the function use of isa() and can() has been deprecated in bleadperl for at least a year. It doesn't answer the question appropriately. It's a hack. It relies on an accidental implementation detail of how method storage works in Perl.

    In my mind, the right solution is to patch overload to answer DOES() appropriately, which has the benefits of being the right question, giving the right answer, and not breaking methods. (Life would be even more pleasant with autobox in the core at that point; then reftype() would be unnecessary before calling DOES().)