in reply to Re: OO-style modifiers for 'sub' ?
in thread OO-style modifiers for 'sub' ?

As other people have pointed out, checking that you are a specific type is just not-Perl. In fact, its not OO either, because it disables inheritance.

All the tests that have been proposed so far work with inheritance. The inheritance problem is when the subroutine isn't called as a method.

C++/Java approach would be to define base classes (as interfaces) and inherit from them. The you can say

sub foo { my ($self) = @_; croak "..." unless ref($self) && $self->isa("AnInterface") }

But this would have problems: First, you may need to check multiple interfaces; second you have to include them in your ISA list; third, its all getting very verbose, and not-Perl.

If you had a base class (or interface) it would be part of the class hierarchy. So you would have:

use base qw(AnInterface AnotherInterface YetAnotherInterface); sub foo { my $self = shift; ... code ... };

Which will be enough (as long as you call foo as a method) to ensure $self is of the appropriate class. Not verbose at all!

I've argued elsewhere that if you don't call foo as a method you deserve all you get :-)

So lets turn it around a bit:

sub foo # requires interfaces InterfaceA, InterfaceB { my ($self) = @_; InterfaceA::validate($self); InterfaceB::validate($self); }

This client code is much simpler: you are passing the implementation of the check down to a module that knows what it means to be that module. An you no longer require explicit inheritance:

I'm covering the same ground as in Class::Interface -- isa() Considered Harmful - but surely these sort of relationships are exactly what ISA is all about?

I don't understand what the advantage of avoiding explicit inheritance is? I've yet to see an example that wouldn't (in my opinion) be better handled by ISA relationships or delegation.

Can somebody show me what I'm missing? :-)

Replies are listed 'Best First'.
Re: Re^2: OO-style modifiers for 'sub' ?
by dpuu (Chaplain) on Jan 31, 2003 at 18:41 UTC
    Can somebody show me what I'm missing? :-)

    Perhaps a good starting point would be to google C++ metatemplate programming. In fact, just consider any simple C++ template function, e.g.

    template<class T1, class T2> bool max(T1 a, T2 b) { return a < b ? b +: a; }
    Any object-pair that supports the less-than operator can be used with this function. There is no explicit C<Comparable> interface -- and there's no loss of static type safety. You ask: "I don't understand what the advantage of avoiding explicit inheritance is?": I'd turn that round: what advantage would be gained by requiring an explicit interface? Once you've convinced yourself that, in the context of C++, the answer is "none": then we can consider the context being Perl. --Dave

      I've coded C++ and grok templates. They solve some problems quite neatly, bearing in mind C++'s design focus on efficiency.

      The thing you lose by having templates like the above is an explicit representation of the classes contract. You have the developer saying "<" has the same semantics in all of the classes involved - rather than it being enforced by the type system. Yes, there is no comparable interface - and that's a mistake.

      So, to answer you question:

      What advantage would be gained by requiring an explicit interface?

      The advantage of having an explicit interface is - that you have an explicit interface :-) By saying something belongs to a particular class you are saying that it functions in a certain way, that it forfills a given contract.

      I might have a class with a method set_pitch(N). If its a subclass of MusicalInstrument I know N is Hertz. If its a subclass of aircraft I know N is degrees. Statements about an objects class stop me doing the wrong thing to the wrong object.

      Templates are handy in C++ but they're not really relevent to perl since it's not statically typed.

      They're also not the only solution to genericity with static typing. For example, Eiffel's generic classes allow more sophisticated statements about inheritance to be made so you can say things like:


      (you can only make SORTED_LISTs out of things that are COMPARABLE).

        OK, I'll admit that I went of at a bit of a tangent there. But since we're here, let me refute your example: an interface named "PitchControl" would be no less ambiguous than a method named "set_pitch". We can't use the ambiguity argument to support explicitly named interfaces.

        But you are right. The discussion shouldn't be about whether it is valid to infer an interface from the body of the implementation of a client (a la templates): its about whether it is valid to infer an interface from the functions supported by an object. That is: we're talking about the dependency structure:

        interface Foo { method fn (...); } class Bar : isa Foo { method fn (...) {...} }
        class Bar { method fn (...) {...} } interface Foo { require method fn (...); }
        The second example states that an object implements the interface if it supports a specific set of methods. It says: "if it looks like a duck, and quacks like a duck, and walks like a duck: then lets assume that it is a duck".

        In both cases, the interface is made explicit. Client code can be written to require an object that implements a specific interface: the interface is explicit from the client's point of view. The binding between the object and the interface is also explicit, but the direction of the dependency is different.

        But your original question still stands: why would we want to break the explicit dependency between the class definition and an interface?

        Imagine you have a blob of 3rd party code that has an overly fat interface. You can't modify this code (e.g. you're not sysadmin). You want to use these blobs in you own code, which uses a much thinner version of the same interface. You want to validate your input (i.e. does the input object implement your thin interface) without modifying the source code of an object that implements the fat interface.

        fanfare: Inferred Interfaces to the rescue! --Dave