Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Inheritance: the root of the problem

by apotheon (Deacon)
on Aug 03, 2006 at 22:36 UTC ( [id://565581]=perlmeditation: print w/replies, xml ) Need Help??

As sort of an academic exercise, I've been thinking about how one could re-envision Perl's object model using closures as objects, without using the bless() function (because otherwise, of course, that's not really re-envisioning Perl's object model — it's just modifying it slightly). The current stumper in this endeavor for which I'm considering possible approaches is how to handle inheritance.

It occurs to me that to play to the strengths of a closure-based object model, one should not simply parrot the implementation of inheritance (or other OOP characteristics) from some other object model. Rather, the concept of inheritance (via mixins, class hierarchy, whatever) should be deconstructed to arrive at a set of base benefit requirements. This might be a menu from which appetizers and entrees need to be chosen according to rules of good taste or (preferably) it may even be possible to boil it all down to a single set of roughly universal basic principles of the benefits of inheritance.

When a client says to you "I need you to design an e-commerce system for me," that's great. You know what you're aiming for in a very general sense, the same way you know what you're aiming for when you say "I need to come up with a means of implementing inheritance in this object model." It doesn't really tell you what that design needs to accomplish in any specific terms, though. You'll have to discuss the needs of the client's web presence and business requirements to deconstruct the larger "e-commerce system" solution into a set of project requirements. That's the sort of thing I'm looking for with regard to coming up with a means of implementing inheritance via a closures-based object model in Perl.

That being said, I'm open to ideas. I have some ideas about how to proceed, but they're half-baked at best and I don't want to pollute the waters from which your mudpuppy-like (edit: I mean that in the best possible way, as a metaphor for "leading to great things from humble origins", or something — okay, so my metaphors need work) suggestions and inspirations might evolve into lungfish, ultimately providing me with the basis for the development of a full-fledged air-breathing mammal (or system of inheritance, as 'twere).

Note that one doesn't necessarily even need to know much about closures, et cetera, to be able to contribute to this. The real point of this meditation is to examine what's needed of a "new" implementation of inheritance. Of course, ideas that apply specifically to what I'm trying to do would be greatly appreciated.


After some comments below, I've refactored the core question of this as follows: What are the fundamental problems inheritance is meant to solve, and how can we solve them in relation to a closure-based object model for Perl 5.x?

print substr("Just another Perl hacker", 0, -2);
- apotheon
CopyWrite Chad Perrin

Replies are listed 'Best First'.
Re: Inheritance: the root of the problem
by chromatic (Archbishop) on Aug 03, 2006 at 23:03 UTC

    I fear that thinking in terms of "inheritance" will lead you down the wrong path. Generalize; what are the problems you're trying to solve for which some OO implementations use inheritance?

    Once you've identified the real problem, then you may have a better idea of what the solution should be.

    (See my Class::Trait module review for some ideas.)

      I'm tempted to say this is a poe-tay-toe versus poe-tah-toe problem. I'm happy with the idea of using something that wouldn't strictly be called "inheritance". I'm starting in my thinking from the direction of trying to figure out what problems inheritance is meant to solve, which answers the question as you phrased it as well, ultimately.

      If you have any suggestions or ideas, I'd love to read them. Failing that, your suggestion for refactoring the question is well taken.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

        This is not "poe-tay-toe versus poe-tah-toe". Inheritance can be useful and it's sometimes appropriate, but it's often a lousy way to build systems, particularly since it tends to constrain code reuse to a given heirarchy.

        If you think of a class as an something which has a responsibility to cover a problem domain, then this class needs to do everything it needs to do. That sounds like a silly tautology, but what it means is that there's an upward pressure on the size of a class. The more things it needs to do, the larger it tends to get.

        However, leaving aside the question of responsibilities, we can also think of objects as units of code reuse. As an object of code reuse, it should do as little as possible. What's the sense of using inheritance when you might inherit twelve methods but only need two? What if, instead of ignoring those other methods, you explicitly don't want them? Or what if you use multiple inheritance you discover you have conflicting methods which ordering problems make difficult to call? Heck, what if you discover that you need to call both of the conflicting methods to maintain proper state in your superclasses? Ack! Many languages suggest using SUPER to get around this, but one keyword doesn't resolve which class you want to call, so you wind up with hacks like hard-coding the superclass name in your method and later wondering why things break when you refactor things.

        Now some of these are issues with poor composition but this is not always the case. Inheritance schemes lead to conflicting pressures of needing both larger and smaller classes. Sure, you can jump through a bunch of hoops to set up delegates to solve problems, but do you really want to do that every time you face this problem? And if you're delegating to another class when you only need a tenth of the features it provides, why even load that other class in the first place? Traits make these problems go away. They're just gone, finished, kaput.

        If you really want to understand the problems with inheritance and how traits solve them, read this traits paper (PDF). Many people (including me) admit that it can take a few tries to see what's going on, but once you get it, it makes perfect sense. chromatic's article shows how easy it is to use traits. The traits paper I linked to is the smoking gun.

        Cheers,
        Ovid

        New address of my CGI Course.

Re: Inheritance: the root of the problem
by tilly (Archbishop) on Aug 04, 2006 at 04:10 UTC
    Decide how you want inheritance to work, and you can implement it with closures. It won't even take much work. Since closures and OO programming are pretty much orthogonal concepts, there isn't really a natural closure way to do OO. They are pretty unrelated concepts. I like to say it as, "OO is about creating good nouns and closures are about creating good verbs." They are just different ways of thinking about the problem.

    The first time I tried to implement an object model with closures was at Re (tilly) 1: Nested Classes. As you can see, it is quite simple. (However I picked a simple model to implement.) At A Cat's eye view of OO you'll find an explanation of that particular inheritance model. If you want some more ideas, there is no shortage of places to look. The Perl 6 design notes, the object models of other languages, CLOS, and so on.

    What do YOU want an object model to do for you?

      Decide how you want inheritance to work, and you can implement it with closures.
      That's just it — I don't know yet, and it's the question I seek to answer by way of this discussion (or, at least, a question I seek to get closer to answering by way of this discussion). There are many here at PerlMonks who know great gobs more about OOP and inheritance-like mechanisms than I, and I seek their (your) wisdom.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

Re: Inheritance: the root of the problem
by stvn (Monsignor) on Aug 04, 2006 at 17:15 UTC
    As sort of an academic exercise, I've been thinking about how one could re-envision Perl's object model using closures as objects, without using the bless() function (because otherwise, of course, that's not really re-envisioning Perl's object model — it's just modifying it slightly).

    Well, one problem is that bless() and packages are the way in which Perl does it's method dispatching. Without hacking the core of Perl and making the method dispatching more "hookable", you will find that you will end up jumping through many syntactic hoops to make things work (or using AUTOLOAD which pretty much rules it out as a tool for "serious" use).

    As a quick aside, you might want to take a look at something I did while working on the Perl 6 metamodel for Pugs, it was a small prototype metamodel based on one written in Scheme. It is basically closure based objects like you suggest, with a slight layer of "sugar" to make them a little more Perl 5 friendly.

    Now, I will skip the rest of your post and go right to the updated question.

    What are the fundamental problems inheritance is meant to solve, and how can we solve them in relation to a closure-based object model for Perl 5.x?

    My first suggestion is to read read read. There are many people thinking about such things, and much time, effort and research has been put into trying to answer this question and then solve it. Here is a quick list of some good resources:

    • Traits

      The people who gave us Traits have done a *lot* of thinking on this subjects, I recommend reading pretty much all the papers on this page. They do a decent job of breaking down the problems of inheritance and show traits as a solution (this and this do a good job of illustrating the real world benefits of their proposed solution).

    • Scala

      The Scala language is an excellent resource for cutting edge OO thinking, in particular with how it can be merged with learnings from functional languages. Some interesting bits of Scala are: Case Classes, Traits and a very nice mixin mechanism. While they still support the traditional OO inheritance model, it has been shown how it's trait and mixin mechanism can be used to solve The Expression problem which traditionally is very messy to solve with inheritance.

    • Slate

      Slate is a prototype-based OO language based on Self, CLOS, Smalltalk with some Dylan mixed in for good measure. It takes an interesting approach in that it's objects are constructed in a prototype-ish fashion, but method dispatch is done with MMD (Multi-Method Dispatch). They demonstrate this as being a very powerful feature in their examples.

    • CLOS

      CLOS (like most things LISP) has been on the cutting edge for about the past 20 years. It's predecesors such as Flavors were invented in the early 80s and are still cutting edge by todays (watered down Java/C++/C#/etc.) standards. The Art of the MetaObject Protocol, which describes the CLOS object model in great depth, is, IMHO, a classic and still relevant today despite having been written in 1991 (lets see how useful all those C# books are 15 years from now).

    • Dylan

      Dylan has been described as "CLOS with an Algol like syntax", which is fairly accurate, but does not really convey the whole picture. Dylan is an extremely powerful language which is sadly fallen into obscurity. Just poking around the reference manual should give you an idea of how totally cool Dylan is :)

    • Scheme

      I don't have any good links for this one, but since Scheme does not come with it's own object model, it has sort of become a right of passage for CompSci PhD types to write their own object models (kind of like what templating systems are for Perl). Simply googling for "Scheme Object Models" will give you tons of stuff, both good and bad.

    • AOP

      AOP (Aspect Oriented Programming) was a big Java buzzword a few years ago, and seems to have lost some of it's steam lately. The idea was that there are some "concerns" which are "cross-cutting" and so therefore do not follow the "direction" of inheritence. There is some merit to this of course, but AspectJ (the sort of de-facto standard in AOP) was basically a compiler pre-processor, and so not truely hooked into the object model itself. It is not suprising that Gregor Kiczales, one of the key figures of the CLOS spec, was also of the fathers of AOP. The metamodel in CLOS basically can do all that AOP can do, and IMHO do it much better. But alas, Java has no programmer accessible metamodel so a compiler preprocessor it was, *sigh*.

    • And lastly, a plug for Moose and Class::MOP

      These are my modules which I have written because I was tired of dreaming of all the nice Perl 6 OO features and having to write crappy Perl 5 OO for $work. They are not (as you are proposing) a re-invention of OO as we know it, but instead a metamodel atop the current Perl 5 OO which should enable much meta-level experimentation. They are heavily based on my study of CLOS and Smalltalk among other things (most of which is mentioned above). It may not be directly relevant to what you are suggesting, but i figure it is worth mentioning at least.

    Now, onto your question.

    Inheritance is not really a bad thing when used appropriately. Some things are really best described with an single inheritence is-a relationship. However, other things are not so simply strutured. But once multiple inheritence comes into the picture, inheritence becomes a really nasty thing. This is where mixins, traits and roles come in with their "sets of behaviors" which can be "injected" into a class (I tend to think of inheritence as being a vertical thing, while (mixin|trait|role)s are a horizontal thing).

    From a not-really-OO-but-kinda-like-OO perspective, there is the Standard ML module system and Haskell's TypeClasses. Here is a link to a fairly well received paper comparing the two, but I have to admit I have not read it yet so I cannot vouch for it. Both these systems take a very "first-class" view of modular units, and create very powerful (and sometimes mind-bendingly complex) systems with it.

    I see all of these things as attempting to solve the basic problem of modular composition, or to phrase it as a question; "How to compose behaviors and state together in a highly reusable fashion?". I think inheritence is simply one part of the solution (albeit a very overused/abused one), and should not be tossed out because it really does have it's place. If properly used, along with other tools such as (roles|traits), mixins and generics, it is a good thing.

    Anyway, lunch break is over, time to get back to $work. Enjoy the links.

    -stvn
Re: Inheritance: the root of the problem
by xmath (Hermit) on Aug 04, 2006 at 16:58 UTC
    Inheritance typically accomplishes two things:

    1. interface inheritance, saying you support every operation that your super supports. This is probably better accomplished with interfaces, either explicit (like Java) or implicit (does it support these methods? good enough.)

    2. implementation inheritance, reusing the parent's implementation of various stuff because the same applies to you. This is can be done via delegation, where you create the parent object, and "merge" some or all of its methods into the "subclass" object you're creating. If there's a need for the parent to call into the child, then appropriate closures should be passed upwards during construction.

    Making a nice syntax for all this is probably the biggest challenge :-)

    UPDATE: You might also want to look at how the E programming language, which does closure-based OO, handles delegation.

Re: Inheritance: the root of the problem
by Moron (Curate) on Aug 07, 2006 at 15:27 UTC
    If it were me, the first thing I would do is come up with a new glossary for such a "rebel" object, e.g. a "clobe" or "imp" or whatever to avoid confusion with Perl's traditional objects (unless you make your programming users swear allegiance to your cause). An object is usually "new"ed, whereas a "clobe" might be "spun", "baked", "smoked" or whatever you fancy or if "imp" then it might by "incarnated" (so perhaps "incarnation" to replace "instance".

    The replacement mechanism for "bless" (..."curse?" ;)) would have to be managed in your own data. But a module-level hash could carry that off. I am not however sure that you can modify a variable in a particular incarnation (closure instance) without having to replace the whole thing and that could be very tricky to code indeed - unless you imposed rules on your imps (or clobes) that would be rather unnatural for closures.

    -M

    Free your mind

      I've actually been partial to the term "clob" since the moment I came up with the idea — the first two letters of each word in the term "closure object". It hadn't occurred to me to invent a whole new jargon for it. That'd be fun, and probably kind of effective if I were to attempt to market the whole idea and sell books about it, but I'm a long way from that right now.

      Let's call them "clobs" for now. More terminology can come later, when I figure out what the specific actions are that would be described by the terms.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

        Gah! No, no, please let us by all means not call them clobs. ;-)



        If God had meant us to fly, he would *never* have given us the railroads.
            --Michael Flanders

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://565581]
Approved by McDarren
Front-paged by virtualsue
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2024-04-24 06:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found