Class::Interface

chromatic on 2003-01-16T01:29:49

I'm not a language purist by any means. I consider myself pragmatic, but realize I'm often idealistic. It's hard to reconcile those two ideas, though.

Consider polymorphism. I've most often seen it explained in terms of inheritance. I'm not a big fan of strong typing in general (at least as C and its descendants do it), but mixing polymorphism with the typing system seems... really wrong. Part of the reason Test::MockObject works is because polymorphism isn't limited to objects that share a common ancestor.

The secret is, objects which share a common interface are isomorphic.

Okay, it's not a very well-kept secret. Still, I've seen (and written) too much Perl code that relies on isa(). (Hey, at least I'm way over checking ref() so often!)

I'd like to see that Perl 6 doesn't encourage falling into that trap. At least, I'd like to see that Perl 6 encourages composition and delegation as well as it encourages inheritance. (Having failed to convince Allison completely through mad rhetorical skills, I resorted to code.)

In that light, I present Class::Interface. Please see the discussion at Perl Monks for more information.

As always, I may be way off track here. Once I figured out the way things really ought to work, though, they made a great deal more sense.


Looks interesting, but...

samtregar on 2003-01-16T04:25:32

Interesting stuff. Wouldn't it be easier to just override isa() though? For example, if I want to tell the world that I implement 'Viewable', then I could do:

  sub isa {
     my ($self, $pkg) = @_;
     return 1 if $pkg eq 'Viewable';
     return 1 if grep { $_->isa($pkg) } @ISA;
     return 0;
  }

On the other hand, by writing a new module you could implement some useful stuff that Perl's OO left out, like compile-time checking of interfaces. For example, if I create an interface called 'Viewable' that requires methods called 'view' and 'hide' then Class::Interface could make sure my class can('view') and can('hide') at compile-time.

-sam

Re:Looks interesting, but...

chromatic on 2003-01-16T06:00:09

Yes, you could override isa(), or much about with the appropriate @ISA. However, per the example in the docs, an Airport is not an Arcade. The Airport has an Arcade. Surprisingly, the semantic distinction is important to me.

Re:Looks interesting, but...

samtregar on 2003-01-16T06:13:45

Isn't the choice of inheritence or composition an implementation detail that should be private to the class? What kind of use case do you have where a caller needs to know whether an interface is implemented using inheritence or composition?

-sam

Re:Looks interesting, but...

chromatic on 2003-01-16T07:55:32

That's exactly the point! Good thinking. :)

Using isa() dictates that the implementation is inheritance (or, at least, that you have to fake up that you're inheriting).

In Class::Interface, derived classes are automatically marked as implementing their parent class interfaces. Whether you inherit or not, by talking about interfaces instead you're saying "objects of this class can (or should) handle the messages an object of this type can". You just have to be a little more explicit if you're not subclassing.

Have you seen ex::interface?

pdcawley on 2003-01-16T08:27:57

Which was my attempt at adding interface support some time back. And which I've since abandoned as I've become convinced that I like optimistic typing a lot thank you very much and that, even though Perl 6 will give you the ability to specify an argument's type, in general you shouldn't bother unless there's evidence that doing so will help things go a lot faster.

Re:Have you seen ex::interface?

chromatic on 2003-01-16T17:53:50

I didn't catch it on my first foray through the CPAN. Now that I've seen it, I see it does something I wanted to avoid.

One of my goals is to support mixins without giving up the possibility of having interface checking. One can imagine a module that provides mixins that, grouped together, form a named interface. Mixing them into a class is simple:

package RobotDog; use Animals mixin => ':Dog';

RobotDog would now be marked as implementing the Dog interface, and it has the doglike methods from Animals. Any code that wants to check that it's dealing with a Doglike object should check that the object implements Dog.

I don't expect often to check typing in order to make my program go faster. I expect to do it to verify that my methods have the proper arguments. Certainly multimethods will help, but I'll lament the lack of much power and flexibility if argument type checking is limited to inheritance.

Compile time checking

malte on 2003-01-16T12:07:15

Hi,

did you think about an (optional) compile time check of interface implementation using a CHECK block?

The module is nice, however, I'd rather have something with the expressiveness of Class::Contract for interfaces, that lets you defined pre-, postconditions and invariants for methods; and a pony.

Might slip in...

Elian on 2003-01-20T03:27:48

I'm seriously considering adding support for this into the Parrot core, though I don't know that anything will actually use it. It seems to me that most OO inheritance things aren't really is or has relationships (though they are both useful) but does relationships--presentations of guaranteed interfaces/protocols/method sets. I mean, who really cares if you're a sub-class of hash, or array, or frob, as long as you present the base hash or array or frob methods?

Re:Might slip in...

chromatic on 2003-01-20T03:41:02

The nice part about does is that if you get it right, you get is for (almost) free! (That's assuming Class::Interface does it correctly, anyway.)

Re:Might slip in...

Elian on 2003-01-20T04:24:28

True, but core support certainly doesn't hurt. :) Besides, having one low-level way to do it makes it more likely that languages layering on top of Parrot are going to use the common infrastructure, and that makes interoperability much nicer.