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


in reply to Why breaking can() is acceptable

Tilly, your arguments make sense to me, but I'm previously unfamiliar with this UNIVERSAL::can; I've never _heard_ of it before, much less used it. I've certainly heard plenty about AUTOLOAD and understand some of the things it would be useful for, though I've never used AUTOLOAD either. But if I ever needed something AUTOLOAD could provide, I might use AUTOLOAD, and up to this point I would not have even thought about this can thing.

So I guess what I want to hear from the people who are saying that nobody should ever break can is, why is it important for every module to work with can? For example, I have a module that we'll call Net::Server::POP3. At this time, it doesn't use AUTOLOAD and so probably doesn't break can, but for the sake of argument let's say I was contemplating using AUTOLOAD in the next release. Explain to me why it's important for my module to work with can. What important thing will users of my module need but be lacking if it doesn't?


;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
  • Comment on What is this can() and why is breaking it (un)acceptable?

Replies are listed 'Best First'.
Re: What is this can() and why is breaking it (un)acceptable?
by stvn (Monsignor) on Apr 06, 2004 at 15:41 UTC

    Quite simply, all classes in perl inherit from UNIVERSAL. UNIVERSAL implements can. Your class then has an obligation (IMO of course) to provide a working version of can in its interface. If you break aspects of your base class in your subclass you are doing bad OO and defeating the whole purpose of re-use through inheritance.

    -stvn

      Your class then has an obligation (IMO of course) to provide a working version of can in its interface.

      If we are talking from an OO purity point of view, I would agree. Anything that breaks inheritance is evil and must be avoided.

      OTOH, a lot of people agree that multiple inheritance is needed in some cases, but there is no good way to implement it. How do you handle dimand inheritance? In what order do you call destructors (something that's been an issue on the Squawks of the Parrot blog of late)? There's no good answer, and an awful lot of bad ones.

      Abigail-II gave a great example in this thread of how easy it is to break inheirtance in Perl (one which I hadn't considered before). If you implement your class as a hashref, all your parents and subclasses also must use a hashref. Which might be the most common case, but it isn't the only one.

      Observation: this thread is a case study in why Perl's object system is hacked on.

      ----
      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

      Quite simply, all classes in perl inherit from UNIVERSAL. UNIVERSAL implements can.

      This is an interesting statement. The index of my Camel book (2nd ed) does not list can at all and contains only one entry for UNIVERSAL, which points to page 293, where I find the following quote (emphasis mine):

      If neither a method nor an AUTOLOAD routine is found in @ISA, then one last, desperate try is made for the method (or an AUTOLOAD routine) in the special predefined class called UNIVERSAL. This package does not initially contain any definitions (although see CPAN for some), but you may place your "last-ditch" methods there.

      This not only doesn't support your assertion but seems on the face of it to directly contradict what you were saying. How can UNIVERSAL implement can, and require that all derived objects (i.e., all objects) not break that, if UNIVERSAL does not initially contain any definitions? Further, NOTHING is said here about any obligations that any module has to provide or support any particular method.

      Granted, what I have is not the latest edition, but none of the reviews I have read of the third edition have said anything about the new edition containing important information about fundamental changes to the language that every Perl programmer must know, nor is it advertised that way by the author or by the publisher. It's simply the next edition of the book, no more. I did see some reviews praising the addition of numerous new examples, but nothing that seemed to indicate to me that if I program according to the second edition I'll break things. To be perfectly honest, I've got a lot of things marked in my camel (not least, little tabs on the sides of the pages to mark where certain sections start so I can quickly flip e.g. to the section on special variables), and not wanting to redo all of that right away I was going to wait for the fourth edition, which hopefully will cover Perl6, before upgrading. Perhaps you could quote me just the paragraph of the 3rd edition that explains why every object is required to support the can method.

      update: In the light of morning, the next paragraph seems harsh to me. I didn't mean it that way. I'm not going to edit it out, though, because the reply wouldn't make (as much) sense then. (And chromatic, I recognize your reputation, but you hadn't yet come out to say anything in the thread when I wrote this.)

      I'm interested in hearing about the merits of can, the practical reasons why it's a useful thing for modules to support, even if the module author does not personally use it. I'm somewhat less interested in hearing you just tell me (in so many words) "This is just required". I might accept that coming from someone who is on a first-name basis with Larry's wife, but I am dubious as to what authority you have to make up requirements for all Perl modules to adhere to.


      ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
        jonadab

        To start with, your Camel book is out of date, but thankfully, http://www.perldoc.com is not. Here is a link to the UNIVERSAL page. You may find this enlightening.

        As for my opinion (and I did make a point to say it was my opinion (note the "IMO")) about OO module designers obligations (please keep in mind it is specific to OO modules, as only blessed references inherit from UNIVERSAL). They are my opinions, you and the whole of the perl community have every right to dismiss them as the ravings of a lunatic (believe you wouldn't be the first).

        But my assertion that it is bad OO design to break the a method in your base class through some fault of your subclass, actually has nothing (directly) to do with Perl at all. It is just OO design, sure its maybe strict OO design, which Perl is certainly not known for, but if you pick up any (good) book on the subject, I am sure you will find something similar to what I say.

        -stvn
Re: What is this can() and why is breaking it (un)acceptable?
by dragonchild (Archbishop) on Apr 06, 2004 at 14:56 UTC
    I'm assuming that Net::Server::POP3 inherits from some basic Net::Server module that provides basic server functionality. Even if it doesn't, let's say that it does.

    Now, let's say that there is some other server base module, called Net2::Server. It provides a very similar (but not identical) interface, but radically different innards. It's useful for different types of servers.

    Now, let's say that I am using five servers, each an object of a class that either inherits from Net::Server or Net2::Server. I don't know which inherits from which. But, I do know that there is one interface difference I need to be aware of - foo() vs. bar(). So, I can do the following:

    my $method = $server->can('foo') || $server->can('bar'); $server->$method( @args );

    And I guarantee that my code will work, regardless of which baseclass the server inherits from.

    ------
    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

      And you've just illustrated where I think that can() can be problematical.

      Despite your claimed guarantee, you didn't guarantee that Net2::Server (or some subclass thereof) won't someday implement a method named foo() that does something radically different from bar(). And then your code will break.

      The problem is that it is as much an abuse of an API to assume that a particular method will never be implemented as it is to use an internal one which may change. Also related is that it is a mistake to assume that what you'd think that foo() should mean will be what someone else will think. This is true even for such obvious values of foo() as a constant named PI. Is that a number or a Unicode character? This related mistake is a meta-problem with can().

      Instead the right way for you to do that is to have some compatibility layer which guarantees that the interface that you want will be supported. That layer could be something as simple as a hash that says that method X is provided by package Y under name Z. Or you can actually implement a class which proxies the methods that you are interested in (incidentally giving you somewhere to put utility methods that one base class has and the other doesn't).

      Now you don't have to worry about the API of Net2::Server changing on you, and you don't need to call can().

        So, to summarize what it is I think you're getting at:

        can() doesn't really serve any useful purpose.

        1. It doesn't really provide usefulness as an interface validator (because interfaces change and because of AUTOLOAD)
        2. Its secondary function of providing a coderef doesn't help, because of AUTOLOAD and because of the questionable use of this

        So, why does it exist?

        ------
        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