> I'm not sure if it helps people understand why I'm confused by explaining
> my background, but I've done exactly zero computer science, and have come
> to whatever (mis)understanding of OO I have by using C++ (and then perl).
> I've never used Java, but I'm aware that it has a concept of "interfaces"
> that can cleanly fulfil most of what C++ programmers end up using multiple
> inheritance for.

(But not all of it; eg. design-by-policy)

> Back to perl:
> 
> I admit that I think of Scalar as being something that conceptually
> multiply inherits from String, Num, Int and "other" (ref), because a scalar
> can be any of these things as it feels necessary. Being from somewhat a C++
> background (oh, and knowing how SVs work in perl5's source), I don't really
> feel that the tree goes the other way, with Scalar at the top. I don't
> expect Int to have a (in perl5-speak) .= method, but if Int inherits from
> Scalar, it's going to have to, isn't it?

Good observation.  I agree.  The problem is that there's a Liskov
substitution that's tripping people up.  An Int can be given to a
Scalar argument, so it must be a subclass.

It may be that Scalar is unrelated, inheritance-wise, to the four
basic types.  Rather, it aggregates and delegates, and provides
conversions to and from them.

> Presumably a Circle class has an attribute radius, which can be set and
> retrieved. And Ellipse has two, semi-major axis and semi-minor axis.
> A circle is an ellipse where the semi-major and semi-minor axes are equal.
> 
> So, if you implement Circle as a subclass of Ellipse this is fine - Circle
> provides the radius methods, so there's no way an Ellipse can have one called
> on it. Good. And if you call Ellipse's semi-major axis method on a Circle
> you get back the same value as radius (or semi-minor axis)
> 
> But what happens if take a Circle (isa Ellipse) and try to set the semi-major
> axis to a value that differs from the semi-minor axis? (I don't know. It
> violates something as I understand it)
> 
> 
> Conversely, if you implement Ellipse as a subclass of Circle, this problem is
> solved. There are no semi-major or semi-minor axes methods, so no chance of
> going wrong there. But as Ellipse isa Circle, it will inherit Circle's radius
> method. What should it return the radius of an Ellipse? (I don't know)
> 
> I don't know the answer to which way up they go. As I understand it, there
> is no right answer.

Well, I think this is one of those subtyping things Larry is talking
about  (Circle doesn't I<extend> Ellipse, it puts a constraint on
it).  But in the most generic world, I would not subclass either.  I
might implement Circle with an Ellipse (spelled C<private> in C++),
but their interfaces are incompatible as you've described.  You can't
use an Ellipse as a Circle, because it has no radius, and you can't go
the other way around, because Circle's can't change their semi-major
axis.

I know this is just a silly example, but in my experience, a common
design mistake is to use inheritance too liberally.  This is a good
demonstration of that.  Circle and Ellipse should be siblings in this
family (perhaps derived from Shape).

> Likewise I'm not convinced about which way round the scalar types heirachy
> goes in perl6. I like it to be both ways up at the same time.
> (in the same universe please)
> Then again, this week is the subroutines apocalypse, not the objects
> apocalypse, so hopefully all will become clear in a subsequent this week.

The more I think about this design, the more I like the solution of no
inheritance relation between them.  I would implement Scalar as a
proxy to whatever lies underneath, stored as references (perhaps) so
changes would be reflected back.

    class Scalar 
    {
        submethod BUILD($.num is rw) { }
        submethod BUILD($.str is rw) { }
        submethod BUILD($.ref is rw) { }
 
        method infix:+= ($self: $rhs) {
            $.num += $rhs.num;
            $.str = $.ref = Undef;
            $self;
        }

        multi infix:as(Scalar $self, Class(Num) $class) 
              returns(Num) is rw {
            return my $x is Proxy(
                for => $.num,
                STORE => { $.str = $.ref = Undef; $num = $_ })
        }  # Using the multimethod, um, method for auto conversion

        # ...

        has Num|Undef $.num;
        has Str|Undef $.str;
        has Ref|Undef $.ref;
    };

Or something like that, maybe.

Luke

Reply via email to