http://qs1969.pair.com?node_id=342819


in reply to Why breaking can() is acceptable

or even having behaviour that is customized per object.

I've got this in one of my production distributions. What did I do? I provided a can() method in that class that uses the same information that AUTOLOAD does. No duplication of information. It's all determined at runtime, anyways. AUTOLOAD needs to figure it out somehow based on the object, so can() should as well.

Personally, it's the module author's responsability to provide and support the implied interface that we are all accustomed to. If you do funky stuff with AUTOLOAD, then you are obligated to do the exact same funky stuff with can(). It's that simple.

I'm going to also stop in its tracks the counter-argument that UNIVERSAL::can() will be broken. Well, you know what? The only reason I can think of to call the UNIVERSAL:: version is for isa(), as a really cool version of ref(). If you're not sure if something is even an object, then why the heck are you asking if it provides a given method?!? I'm not sure I would be able to follow such obfuscation. It sure as hell better not be in my codebase cause it ain't gonna pass my code review.

(Personally, I don't like it when people call UNIVERSAL::isa(), either, cause it prevents me from doing cool things, like pretending I'm not really implemented as an ARRAY in order to fool HTML::Template into treating me like a SCALAR and stringifying me. Scalar::Utils::blessed() is a much better option, but blessed() didn't come into the core till 5.8.x. *shrugs* *makes a note to use blessed() more, now that he ranted about it*)

------
We are the carpenters and bricklayers of the Information Age.

Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

Replies are listed 'Best First'.
Re: Re: Why breaking can() is acceptable
by tilly (Archbishop) on Apr 06, 2004 at 04:56 UTC
    Personally, it's the module author's responsability to provide and support the implied interface that we are all accustomed to. If you do funky stuff with AUTOLOAD, then you are obligated to do the exact same funky stuff with can(). It's that simple.

    Is it also the module author's responsibility to not break multiple inheritance?

    The way that you described doing things underscores my point. If your funky class is in a later chain of inheritance with multiple inheritance, then you just broke can! Exactly what you said that it was your responsibility not to do!

    Depending on how yhou implemented it, you may have broken can with single inheritance as well. (Particularly if the subclass tries to use AUTOLOAD. But one could argue that it is that subclass' responsibility to understand the class that it is overriding.)

    Which is one of the reasons why I kept on saying UNIVERSAL::can. Overriding can in any other location leads to potential breakage. Personally I'm OK with that as long as you're clear on what breaks, and why. (I'm not a big fan of multiple inheritance, so I don't grieve when it dies. Others might.)

    The dominant reason why I said it that way was to keep people from having to figure out whether I was talking about can as a noun or a verb in the same sentences. However the other issue was in the can of worms that I referred to if you try to override can locally. (Hrm, make that a verb or which noun? :-)

      tilly

      First, I will say that if you are using multiple-inheritance in the presence of AUTOLOAD then you are already in trouble. Sure, you should be able to do it, but IMO thats being idealistic. Part of the difficulty of multiple inheritance is managing the dispatching of methods and name clashes, if your methods aren't even implemented (and handled with AUTOLOAD) your just asking for it.

      As for how to implement local can without breaking it elsewhere, I don't see what the problem is. This (untested) code below (written waaaay past my bedtime) should serve as a stating point:

      sub can { # ------------------------------------ # this is the code tilly is talking about below # sorry , it was late # ------------------------------------ # my ($calling_package) = caller(); # if ($calling_package eq __PACKAGE__) # ------------------------------------ # however, this is what I meant ... my ($self, $method_name) = @_; # if this is called with by an object specifically # blessed into this __PACKAGE__, then we # will handle this because of AUTOLOAD if (ref($self) eq __PACKAGE__) { return my_special_can_that_plays_nice_with_AUTOLOAD(@_); } else { return $self->SUPER::can($method_name); } }
      Now I am making the assumption that SUPER::can will work, but its alot easier to control where you inherit from then it is who inherits from you. If SUPER::can doesn't work, then do something that does work.

      I will say again, can should work, period, end of story. If it doesn't work (because you used AUTOLOAD or even some symbol table madness) then you need to re-think your design.

      -stvn
        With your code snippet, if someone has an object of your class (or a class that inherits from your class) and wants to know if you can foo, where it can foo because of your AUTOLOAD, then your can will say that it can't foo because the person calling can is not making that call from within your class.

        Again, this is not as easy as it looks.

        Furthermore allow me to point out that AUTOLOAD predates can by many years, and is far more widely used. Is code that was written before they decided to start populating UNIVERSAL:: in Perl 5.004 broken because Perl 5.004 came out? Does code written by someone familiar with Camel ed. 2 need to be rethunk because p5p decided that people might like to have a feature that most people don't use?

        My belief is that p5p adding to Perl should not invalidate accepted practices. My further belief is that of AUTOLOAD and can, AUTOLOAD matters more to most Perl developers. Going further, my impression is that people who are familiar with other dynamic languages are more likely to want to reach for something like AUTOLOAD (perhaps they'll call it method_missing, but they expect it to be there) than they will can.

        My conclusion, therefore, is that someone who wants developers to not break can with AUTOLOAD is fighting an uphill battle. Furthermore, knowing everything that I do, my sympathies are with the person who wants to use AUTOLOAD to full effect. (Though admittedly I'm more likely to creating a bunch of functions by typeglobbing some closures rather than use AUTOLOAD. Unless I have a reason to use AUTOLOAD.)

      Is it also the module author's responsibility to not break multiple inheritance?
      Is there any module on CPAN that doesn't potentially break with multiple inheritance? Any object that uses a reference to a hash, and stores its attributes in that hash potentially breaks multiple inheritance. In more than one way.

      Now, if everyone just used inside-out objects.... (but they don't magically solve the AUTOLOAD/can mess).

      Abigail