RFC 265 (v1) Interface polymorphism considered lovely
[email protected] (Perl6 RFC Librarian) 20 Sep 2000 19:54:45 -0000
| Newsgroups | perl.perl6.language.objects,perl.perl6.announce.rfc |
|---|---|
| Message-ID | <[email protected]> |
This and other RFCs are available on the web at http://dev.perl.org/rfc/ =head1 TITLE Interface polymorphism considered lovely =head1 VERSION Maintainer: Piers Cawley <[email protected]> Date: 20 Sep 2000 Mailing List: [email protected] Number: 265 Version: 1 Status: Developing =head1 ABSTRACT Add a mechanism for declaring class interfaces with a further method for declaring that a class implements said interface. There should be (at least) runtime checking to ensure that a class actually *does* implement said interface, with an option to have compile time checks made. =head1 DESCRIPTION =head2 Rationale To (mis)quote Damian Conway "Inheritance polymorphism proliferates pretend classes". The idea is that C<my Dog $spot> will eventually give us some real performance gains, so more folks will want to use it. But this leads to a proliferation of 'ghost' base classes that are simply there to provide a handle for C<my Foo $bar> optimization. So, I propose that we move such classes sideways and make them into first class interfaces. By supporting interface polymorphism the way is opened for adding more stricture in projects that *need* it, as well as allowing for the possibility of compile time optimization for classes that use the interface structure. <This needs some more fleshing out> =head2 Syntax My current thought is that an interface file would look something like: interface Fetcher; sub fetch; Note the new keyword, interface, this works just like package but with the restriction that it can only contain subroutine declarations. A Class that implements the interface would then look like: package TrainedCat; use strict 'implementation'; use strict; use base 'Cat'; use interface 'Fetcher'; sub fetch { my TrainedCat $self = shift; my $thing = shift; return $self->can_lift($thing) ? $thing : undef; } Note the C<use strict 'interfaces'>, this declaration implies that interface conformance shall be checked at compile time. If the C<TrainedCat> class doesn't implement the C<Fetcher> interface in full then compilation shall fail. Without this stricture constraint the compiler shall throw a warning. Of course, Perl being Perl and all lovely and dynamic, there's a good chance that some of the methods that implement the interface aren't actually compiled up at the end of the compilation phase. Therefore we have: package LazyDog; use strict 'implementation'; use interface 'Fetcher'; use deferred qw/fetch/; ... sub AUTOLOAD { ... *fetch=sub {...}; goto &fetch; ... } C<use deferred> informs the compiler that the method will be available when it's needed, even if it isn't actually in the symbol table/class hierarchy just yet. In client code, lexicals that are typed into interfaces only require that the objects assigned to them satisfy the interface. ie. They don't care about an objects class, they just care what it can do. (If only life were like that...) use strict 'interfaces'; my Fetcher $x; my Dog $spot; my NetSnarfer $z; my $method = 'fetch'; # Compile time stuff $x = $z; # ok because $z can fetch $x = $spot; # ok because $spot can fetch $x->fetch(); # ok because Fetcher->can('fetch'); $x->bark(); # not ok because ! Fetcher->can('bark'); # Runtime stuff $x->$method(); # ok because Fetcher->can('fetch'); $method='bark'; $x->$method(); # runtime error because ! Fetcher->can('bark') Without the C<my strict 'interfaces'> declaration, these errors get demoted to compile time warnings. Of course, without the stricture any compiler optimizations become somewhat tricky. =head2 Another possible syntax In message <[email protected]>, Damian Conway suggested another possible syntax: my $foo : must (feed water play poop) $foo = Manager->new(); # die "Manager object can't play" An interface then becomes an alias for a particular C<must> use attr_alias Pet => qw(feed water play poop); my $foo : must(Pet); Personally I'm not too keen on this. I think the C<my $foo : must(Pet)> syntax is ugly and somewhat ambiguous unless you start adding rules to make aliases begin with C<:> by analogy with import tags. Also this syntax makes it hard to have a class that implements parts of the interface via an AUTOLOAD (though I suppose C<use deferred> is still a possibility it goes against the E<nice> thing about the proposal, namely that a class's user can roll his own interface requirements) Nathan Torkington suggested still another syntax: package Cat implements Pet; package Dog implements Pet; Which is kind of neat. =head2 Implications? Because the compiler will know about available methods for typed variables it may become possible to have Damian's named argument magic for method calls, though this is a tad scary to me... =head1 IMPLEMENTATION I have some ideas about this, and I believe it may be possible to do a good chunk of the runtime/compile time behaviour in pure perl, but if we are to gain performance gains for more than just the programmer (though it's my contention that the potential time savings available to the programmer who's bug hunting are reason enough for this change) this stuff should be usable in core. And I know little of the core. =head1 MIGRATION I'm not sure that there are any migration issues, what with this being a new bunch of syntax and all... =head1 REFERENCES RFC 218 C<my Dog $spot> is just an assertion Some of the subsequent discussion of this RFC contains some discussion of adding interface polymorphism to perl. Java. Java is one language that springs to mind that uses interface polymorphism. Don't let this put you off -- if we must steal something from Java let's steal something good.