Dear monks,

after having been contributor on a number of large programming efforts I have learnt to see the value of interfaces. For those of you who haven't been in contact with those: an interface is a class that defines a set of methods, without actually implementing any of them. By inheriting from this class, you essentially enter into a contract where you promise to implement the methods defined in the interface. Other bits of code can than simply test whether $your_object->isa('Some::Interface::Class') and invoke the methods as defined in the interface.

As an actual example: I am the maintainer of Bio::Phylo. I'd like to be compatible with BioPerl, and in particular with the Bio::Tree::TreeI and Bio::Tree::NodeI objects. Hence, I check at the top of my code whether those interfaces are installed:
eval { require $interface }; if ( ! $@ ) { push @ISA, $interface; # implement interface methods. }
...and with a bit of symbol table manipulation I implement the methods as defined in the interface.

I think there might be some value for the perl community in adopting certain interfaces in a broader manner. For example, wouldn't it be handy if there was a cpan module Class::Interface::Iterator that all objects that are iterators, no matter the problem space, implement? I think this would be a great way to simplify apis, and with conditional, runtime inheritance manipulation and aliasing you don't introduce unnecessary dependencies or backwards incompatibilities.

I know it'd probably never get to that seeing the independent-mindedness of the perl community. But it would be nice, wouln't it?

Replies are listed 'Best First'.
Re: Interfaces for the masses!
by diotalevi (Canon) on Mar 23, 2006 at 00:56 UTC
    an interface is a class that defines a set of methods, without actually implementing any of them.

    No, that's what Java thinks "interface" means. We have better things in perl. In fact, we have the freedom to define as much or as little of the that API (because that's really want you asked for). We can use subclassing, delegation, mixins, or anything else that fits our code.

    [ Added You should look at the Dumper/Storable modules and things that implement freeze/thaw for examples of how perl has use interfaces successfully.]

    Also, we already have two iterator interfaces: the tieable file handle and closures. I'd rather not see a "Class::Interface::Iterator" because I'm sure it's going to be more verbose and less useful than the tools we already have in common use.

    [Updated: there was originally an interesting Freudian slip and I've removed it.]

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      No, that's what Java thinks "interface" means. We have better things in perl. In fact, we have the freedom to define as much or as little of the that API (because that's really want you asked for).
      I don't think that was quite what I asked for. The issue lies not so much in how much / little has been implemented, but that there could be scope for a contract such that $obj->isa('Interface'); tells you more than duck-typing based on a single method call could. Outside of implementation details. Isn't that a good idea under some circumstances, even if my understanding of what an interface is comes from Java?
      Added You should look at the Dumper/Storable modules and things that implement freeze/thaw for examples of how perl has use interfaces successfully.
      I just looked at Data::Dumper's and Storable's code. I don't see what you mean.
      Also, we already have two iterator interfaces: the tieable file handle and closures. I'd rather not see a "Class::Interface::Iterator" because I'm sure it's going to be more verbose and less useful than the tools we already have in common use.
      Okay, the iterator interface might be a bit over the top seeing that perl has wonderful list processing capabilities. But what about other patterns? I imagine general 'Model', 'View' and 'Controller' interfaces would be handy for the Maypole/Catalyst crowd?

        If you wrote UNIVERSAL::implements and made it take a contract name, you'd have what you want. I think I'm more likely to use Params::Validate to do my duck-type checking. Not that it's seemed all that important in practice. Occasionally I've needed ->can() and if the object has a method of the appropriate name, that's good enough. I've never seen an occasion where an object implements something wrong under the same name.

        Normal duck typing

        use Params::Validate 'validate_pos'; use constant FOOBAR_TYPE => { can => [ qw[ print flush frobnicate ] ] +}; sub bar { my $foobar = validate_pos( @_, FOOBAR_TYPE ); }

        Your stricter version.

        use Params::Validate 'validate_pos'; use constant FOOBAR_TYPE => { callbacks => { interface => sub { Interf +ace::has( FooBar => $_[0] ) } } }; sub bar { my $foobar = validate_pos( @_, FOOBAR_TYPE ); } package FooBar; use Interface 'FOOBAR'; ...

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: Interfaces for the masses!
by Tanktalus (Canon) on Mar 23, 2006 at 04:58 UTC

    Personally, I just test $obj->can('foo'), and, if it can, great, I call it. If it can't, then I take appropriate evasive maneuvers. Which may be to die, or it may be to skip that function call.

    Of course, if it's just to die, I usually let the perl interpreter do that for me, although sometimes I like some extra debugging info for the poor sap who gets to maintain the code after me and add new objects to the overall infrastructure.

      Just because $obj->can('foo') doesn't mean that $objs idea of foo matches mine ... that's where documented, named, interfaces (or "contracts" if you want a less java centric term) come in handy.

      You have an explicit agreement not just that $obj->can('foo') -- but that it means what you think it means.

        In many ways, that's the beauty of perl. Unlike some other languages (oft referred to as "B&D" languages for the tight restrictions they place on you, and the penalties for trying to do something outside of their box which can be severe), Perl is good at letting you do things that the author of the code never thought you could do.

        My favourite examples have been one of my coworkers who was tasked with creating a certain module. He placed enormous amounts of defensive code in there to ensure all the parameters were passed in the way he envisioned that they should be. After a few code reviews, I had most of those removed, and then showed him a piece of code I wrote to use his module in a way that he couldn't imagine was supposed to work. But because I could overload, override, and generally muck about, creating interfaces that didn't quite do what he thought they should, I could take advantage of his module to do some core piece of work for me. And, like all good cases of code reuse, when bugs were fixed or features added, I got them for free.

        He wasn't a complete novice in perl programming, but has never been as "plugged in" to the community as I have been, and simply had not been exposed to much of the power of perl. And I wasn't doing anything extremely unique, either - passing in handles to "stringy" files or even just \*DATA, where he was expecting everything to be real actual files. He was just amazed at what I could do with his interface if he'd just relax and let me do it.

        I used to write B&D code, too. But even in C++, which is still among my favourite languages, if not my outright favourite still, I always found restrictions on what I could do annoying, while perl's free-for-all has been quite liberating. What I've found is that B&D programming assumes that the person writing the library (or reusable code) is smarter than the person using the library, while Perl accepts that the user may be smarter than the library/module author.

        If I really want to know if some object ISA different type, I'll check via the isa method. But usually, I just can if the object CAN do something, not why it can do so. Then I trust that when I call that method, it'll do something useful, and that the person who gave me that object in the first place knows what s/he is doing. After all, that person may just be smarter than me. (At least as far as perl and/or programming is concerned ;->.)

      I do that too - but it doesn't give you the same amount of context as knowing that a particular object implements an interface.
Re: Interfaces for the masses!
by itub (Priest) on Mar 23, 2006 at 01:36 UTC
      Thanks, just checked it out. Seems interesting, though my sense is that interfaces only become handy if they're explicitly advertised and documented, not defined inline.
Re: Interfaces for the masses!
by adrianh (Chancellor) on Mar 23, 2006 at 10:18 UTC
    I know it'd probably never get to that seeing the independent-mindedness of the perl community. But it would be nice, wouln't it?

    Well - interfaces are not the only solution to that sort of problem. IMO the Java interface style are are not particularly nice hack to get around problems with multiple inheritance.

    Instead of interfaces Perl seems to be heading towards better ways of doing MI with traits/roles - see Class::Trait for example.

      traits/roles are going to be a boon. In a way, though, by saying that some class does some role you are entering into a contract not completely dissimilar to interfaces, right? (That is, if I understand what I've read of the Apocalypses and Synopses correctly.)

        An interface (as you describe it, similar to the Java concept) is a degenerate case of a role. A role can provide default (and even parametrized) implementations of the appropriate methods and can disambiguate dispatch at compile time.

        Hopefully, Perl 6 will even also be able to dispatch multimethods based on roles.

Re: Interfaces for the masses!
by duff (Parson) on Mar 23, 2006 at 15:28 UTC

    "The Perl community" is a collection of many other overlapping communities that all just happen to have perl in common. I'm sure some subset of the Perl community will think your idea has merit, so the way to garner support is through implementation. Write code. Put it on CPAN. If it's useful, people will use it. (Especially if you advertise by giving talks at perl conferences or write tutorials for perl.com, etc.)

    If you're looking for something more than just putting it on CPAN, then you'll have to do more for the community. Keeping with your example, show why having Class::Interface::Iterator is beneficial to the perl community. Show how it would typically be used. Show how "broad adoption" is important to the benefits it provides. Show how having it won't hurt (e.g.; make everyone pay a speed penalty for the benefit of just a few). Convince the appropriate perl gurus to agree with you. Et cetera.

Re: Interfaces for the masses!
by gaal (Parson) on Mar 23, 2006 at 16:02 UTC
      Perl6::Roles might do what you're looking for. The rewrite of DBI is using it extensively.

      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Interfaces for the masses!
by dokkeldepper (Friar) on Mar 23, 2006 at 17:03 UTC

    What about abstract classes? One can always prepare empty methods in a class that croak if they are not overwritten. The usage of SQL::Statement in DBI provides an example.

    BTW Java Interfaces are mostly used to mimic multiple inheritance or as a marker ("this class can this or that").

    Both usages are not needed in perl because it can be done directly.

      That is how 'interfaces' are created in BioPerl. Bio::Tree::TreeI defines the methods any if its implementors must provide. It croaks if the methods are called directly on the interface.
Re: Interfaces for the masses!
by acid06 (Friar) on Mar 23, 2006 at 17:42 UTC
    While I completely agree with Tanktalus previous (rather long) post, I don't think the concept of interface is completely useless in the Perl world.

    I think it'd be nice if there were some kind of "soft" or "advisory" interfaces. They wouldn't force anything, they'd be just a standard way of querying if the module implements a well defined set of functions.

    An appropriate name for this would be UNIVERSAL::implements, so you'd be able to query classes for some specific behaviour like:
    if (Class->implements('interface')) { # do stuff }
    Then you'd have a way of each class to specify which interfaces they implement and a simple way of defining these interfaces. It would be just a well specified way of programming by contract.

    I think this sounds like a reasonable idea, but might just be overkill. I don't really know.

    I just wanted to note that I think this is one of the true beauties of Perl: in other languages this discussion probably wouldn't even exist since it'd be something impossible to do.

    Update: typo fixed.


    acid06
    perl -e "print pack('h*', 16369646), scalar reverse $="
      An appropriate name for this would be UNIVERSAL::implements, so you'd be able to query classes for some specific behaviour...

      With Class::Trait you'd do:

      if ( eval { Class->does( 'interface' ) } ) { # do stuff }
      Then you'd have a have of each class specifying which interfaces they implement and a simple way of defining these interfaces.
      Do you mind clarifying the typo? I was just getting into your post and now it feels like I'm missing the punchline.