Settings

Theme

Prototype based vs. class based inheritance

stackoverflow.com

35 points by brandonkm 15 years ago · 25 comments

Reader

silentbicycle 15 years ago

See: http://www.c2.com/cgi/wiki?ClassesPrototypesComparison

In my experience, prototype-based OOP is less prone to the typical OOP over-analysis - if you just need one object that does something, you just assemble it, that's it, problem solved. Since the default stance isn't designing a generic class for all possible subtypes that exhibit related behavior and blah blah blah, there's less push toward overthinking things.

Prototype-based OOP is also well-suited to runtime modification, since the underlying implementation is usually simpler, but that's a distant second to the above benefit. And while prototypes are sometimes assumed to be less efficient than classes, look at the research from Self* - While some popular languages have poor implementations, it's not inherently less efficient.

Also, I suspect prototypes integrate more smoothly with non-OOP code than classes, based on experience with Lua (which isn't strictly OOP, but typically uses prototypes). To some extent, this blurs with dynamic typing, though - there's only one statically typed + prototype-based OOP language I know of, Günther Blaschek's Omega (described in _Object-Oriented Programming With Prototypes_).

* http://selflanguage.org/documentation/published/index.html Some of the people involved later worked on the "JVM". Perhaps you've heard of it?

  • demallien 15 years ago

    In my experience, prototype-based OOP is less prone to the typical OOP over-analysis - if you just need one object that does something, you just assemble it, that's it, problem solved. Since the default stance isn't designing a generic class for all possible subtypes that exhibit related behavior and blah blah blah, there's less push toward overthinking things.

    But that's not really a property of prototype languages - it's a property of duck typing. For example, Ruby - a non prototype language - can implement exactly the same idea. Take an instance of a class, and add a method specifically to that object - no problems. Critically, the thing that makes this possible is the fact that evaluation of the existence of the method is made at call time, not at compile time.

    • silentbicycle 15 years ago

      Dynamic and duck typing does help quite a bit there, but I think the default stance of prototypes rather than classes also makes a difference.

      The way I write object-centric code in Lua feels very different from in Python, even though both languages are dynamically typed (and otherwise fairly similar). It probably has to do with conventions / what's "Pythonic". I haven't used Ruby enough to comment.

  • uros643 15 years ago

    See also Lisaac (http://en.wikipedia.org/wiki/Lisaac), another prototype-based OO language that's statically typed.

russellallen 15 years ago

Some thoughts:

Given a sufficiently flexible and late-bound class system (like Smalltalk's), the differences between prototypes and classes can blur.

But in general I would say that it is easier to build classes on top of prototype systems than vice versa, which is one argument for prototypes.

All prototype implementations aren't the same, though, any more than Smalltalk and C++ both have the same class systems! Javascript and Self are quite different in some ways, especially when it comes to delegation (ie inheritance) and iolanguage and research such as Kevo are different again.

Despite the first answer to the linked question, I don't think that either class based or prototype based languages are easier to write a VM for.

What attracts me most is that prototype systems are conceptually simpler in an Occam's Razor sort of way. Instead of needing two concepts: classes and instances, we only need one: objects.

  • wvenable 15 years ago

    > What attracts me most is that prototype systems are conceptually simpler in an Occam's Razor sort of way.

    That's true, but I find you end up having to make the difference. Yes, prototype inheritance is conceptually simpler but in practice it's more complex. In JavaScript, for example, OO code tends to be harder to follow and includes more boilerplate code. You have to make up for the simplicity of the platform in order to get your work done. In more traditional OO languages, inheritance might be more limited but it's also more straight forward and easier to implement, calling parent class methods is build in, and the "this" reference works consistently.

    • wkornewald 15 years ago

      JavaScript has the worst form of prototype inheritance I've ever seen. All the boilerplate code is not the fault of prototypes, but of JS. Also JS prototypes are insanely limited. You can't have multiple delegates and you can't change delegates at runtime. JavaScript's inheritance actually smells like some broken state between class based inheritance and prototypes. It has none of the advantages of either and combines the disadvantages of both. I'll make a longer blog post explaining how prototypes are supposed to work and what their real advantages are. You'll see that JS inheritance is a huuuge design failure and that nobody should ever mention prototypes and JavaScript in the same sentence.

      • wvenable 15 years ago

        I've studied other prototype languages (Io for example), but JavaScript is the only one that I use on a regular basis or in a professional capacity. I imagine for the vast majority of developers, JavaScript is their only introduction to prototype inheritance.

        • wkornewald 15 years ago

          Yeah, and that's a real shame because those developers will get a really bad impression of prototypes (unless they only use one of the class emulation libs and never learn about prototypes, of course).

    • btilly 15 years ago

      In more traditional OO languages, inheritance might be more limited...

      Explain.

      Class based inheritance, with a good metaprogramming model, is strictly more flexible than prototype based inheritance because you can easily implement the latter. The shortest implementation that I know of is the following Ruby one:

        Proto = Class.new(Class)    # Beware: magic.
        def Proto.clone
            Class.new(self)
        end
      
      
      You can find out more about how to use that at http://snippets.dzone.com/posts/show/3378.
      • troels 15 years ago

        Well, Ruby's classes are open. So while they are called classes, they are much more similar to prototypes than, say, Java's classes are.

        • btilly 15 years ago

          FYI, Ruby's class model is almost directly copied from Smalltalk, which is the classic object oriented language, and is where the phrase "object oriented" was invented. Therefore there are no grounds to suggest that Ruby somehow does not have "real" classes because they don't look like Java's.

          • troels 15 years ago

            I weren't proposing any form of value with my statement. Just pointing out that on the scale of {run-time..compile-time} object model, Ruby is closer to the run time end.

    • wladimir 15 years ago

      Javascript also severly biased me against prototype-based and for class-based inheritance.

      Then again, Javascript's implementation of anything is awful. The only reason people use it, is because there is no way around it for web client programming. I'm pretty sure we shouldn't take it as an example.

    • russellallen 15 years ago

      This may be true but if it is I think it must be specific to JavaScript. It's the opposite to my experience with Self, which is that prototypes help my code be clearer and simpler.

  • BigZaphod 15 years ago

    I've always understood it to be that prototypes are a superset of classes.

mln 15 years ago

this is what Guy Steele has to say. http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...

  • eliben 15 years ago

    One of the links from this page mentions this: http://okmij.org/ftp/Scheme/oop-in-fp.txt, where the author presents a simple example of implementing an object using closures:

      (define (make-point-2D x y)
        (define (get-x) x)
        (define (get-y) y)
        (define (set-x! new-x) (set! x new-x))
        (define (set-y! new-y) (set! y new-y))
        (lambda (selector . args)     ; a dispatcher
            (case selector
              ((get-x) (apply get-x args))
              ((get-y) (apply get-y args))
              ((set-x!) (apply set-x! args))
              ((set-y!) (apply set-y! args))
              (else (error "don't understand " selector)))))
    
    Which is curiously similar to the canonical object implementation of SICP in section 3.2:

      (define (make-account balance)
        (define (withdraw amount)
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds"))
        (define (deposit amount)
          (set! balance (+ balance amount))
          balance)
        (define (dispatch m)
          (cond ((eq? m 'withdraw) withdraw)
                ((eq? m 'deposit) deposit)
                (else (error "Unknown request -- MAKE-ACCOUNT"
                             m))))
        dispatch)
  • cwp 15 years ago

    Actually, Anton van Straaten said that. Classic.

  • dchest 15 years ago

    A closure is an object that supports exactly one method: "apply". - Guy

bgriggs1 15 years ago

Here's a useful comparative demo that makes this a little easier to differentiate: http://alexsexton.com/inheritance/demo/

yycom 15 years ago

Can someone point to a non-trivial example using prototypal inheritance?

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection