> 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