golang OO removal, benefits. over python?

Chris Angelico rosuav at gmail.com
Mon Mar 10 17:01:13 CET 2014


On Mon, Mar 10, 2014 at 6:50 PM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> The author points out that nested structures can be made optional by
> including a pointer to the structure instead of the structure itself.
> Again you can do the exact same thing in C++; in OOP this is usually
> described as a "has-a" relationship.  Most languages don't support the
> syntactic sugar that makes this look almost identical to inheritance
> in Go, but that's all it is.

Composition versus single inheritance versus multiple inheritance.
Where's the line drawn? In Python, it's easy to tell one from another.
Composition hides everything behind another dot level; single and
multiple inheritance use the MRO (with MI handled by "super" sometimes
pointing you to a peer). But compare this:

class command
{
    void create(string name) {.......}
    void process(...) {...}
}

class hook
{
    void create(string name) {.......}
    void inputhook(...) {...}
}

class timer
{
    inherit command;
    inherit hook;
    void create(string name) {::create(name); .....}
}

This is part of the class hierarchy of Gypsum, implemented in Pike. It
looks like multiple inheritance, and it mostly works that way. Given
an instance of timer, you can call process() on it as a command, or
inputhook() as a hook. The create() function (which is like Python's
__init__) defers to its parents using a C++ syntax form with the
leading double colon - but without C++'s ambiguity error. (The
specific case of the constructor is different in C++, but any other
duplicate method name would be a compile-time error. In Pike, the
expression "::create" actually returns an array of functions, in the
order of the inherits; and calling an array of functions calls each
function in turn with the same args.) So this definitely acts like MI.
And the syntax is almost the same as SI, in that removing the "inherit
hook;" statement will have this function beautifully as
straight-forward single inheritance. But consider this:

class foo
{
    inherit Stdio.File : file1;
    inherit Stdio.File : file2;

    void readwrite()
    {
        //Assumes both files were opened by other methods
        file1::write(file2::read());
    }
}

So multiple inheritance is really just composition in disguise.
(Actually, this last form is stylistically discouraged. It's normally
much clearer to use a more classic form of composition, which works
the same way Python's does. But it does work, and there are uses for
it.)

I think it's only the theory purists who really care strongly about
the differences between all the various terms. There's an implication
to inheritance (Liskov Substitution Principle) that composition
doesn't have, but *every* (bar none!) implementation of Multiple
Inheritance I've ever seen is either horribly horribly sucky, or ends
up creating some edge cases that make LSP a little odd. (Or both.) So
don't sweat the details. Practicality beats purity. I think C++'s MI
implementation is more on the pure side and less on the practical...
and I've never used it in production code. Python's MI is fairly
practical, but requires that every class in the hierarchy be able to
cope with MI, and has odd edge cases with "might be the last in the
tree". Pike's MI has oddities with external "reaching in" to
something, so sometimes I need to write explicit dummy methods, or
carefully avoid name collisions; definitely practical, and quite
useful. Java's MI simply doesn't exist (that's perfectly pure!),
although implementing interfaces can do a lot of it; but then you end
up duplicating piles of code (on the other hand, that's nothing new in
Java code). I don't remember what other languages I've tried MI in,
but most of them tended toward the "pure" and were impractical enough
to avoid using.

ChrisA



More information about the Python-list mailing list