Allow me to try to add some more context...
[tl;dr]
⢠Inheritance is about data structuring. The only difference between Prototype-based Inheritance and Class-based Inheritance consists of RULES about WHEN it is legal to structure and re-structure data.
⢠âSelfâ corralled prototypal inheritance. âSmalltalkâ corralled class-based inheritance. Both, borrowed from previous ideas.
⢠All big inventions in programming stem from the use of dynamic languages.
⢠Class-based inheritance is Waterfall Design. Protoype-based inheritance allows iterative design.
⢠JavaScript was based on Lisp. JavaScript designs-in prototype-based inheritance better than Common Lisp, Scheme, Clojure, Racket, etc., etc.
[background]
In the beginning there was assembler.
There are 2 types of assembler
1. line-oriented
2. tree-oriented
Assembler is characterized by
⢠ultra-simple syntax (usually prefix notation)
⢠commands with operands.
Line-oriented assembler is what we call âassemblerâ.
Tree-oriented assembler is what we call âlispâ.
Assembler gives you a toolbox of functionality, but, if you want to structure your data you have to do it manually.
In assembler, nothing stops you from re-structuring your data on the fly.
The result, though, can be hard to understand. This is part of what we call âreadabilityâ. The term âreadabilityâ is usually used to mean human readability (aside: but, thereâs also machine readability ; note that textual âreadabilityâ does not mean the use of general English prose, but, only a well-defined, restricted subset of English)
One of the tennets of Computer Science is Donât Repeat Yourself (DRY).
Inheritance is simply a way of structuring data in a DRY way. If youâve structured your data, donât do it again, copy the template.
So-called âprototypal inheritanceâ is a way of structuring data that can allow changes to the structure at runtime.
âClass-based inheritanceâ is an optimization of prototypal inheritance. In class-based inheritance, you separate your Red Smarties from the rest (a Smartie is an M&M, a âred Smartieâ tastes âbetterâ). If you apply the class-based optimization, then, you can compile-out data structuring. The compiler can help make your resulting code tighter and more check-able, but, you are allowed to structure your data ONLY at compile-time. And, the result is less-confusing. Nothing should change at runtime. Dynamic-anything is âbadâ (aside: this is a fundamental problem with pub/sub).
Optimization culls creativity, but, results in a notation that has certain âhuman readabilityâ properties. Optimization is âbadâ during Design (âpremature optimizationâ), but Optimization is âgoodâ during Production Engineering (what we mistakenly call âprogrammingâ).
Self and Smalltalk are syntaxes draped over assembler. Self does not insist that you pre-define all data structures, Smalltalk does insist on pre-definition. The tools for inheritance always existed, but Self coined the phrase âprototypeâ and Smalltalk coined the phrase âclassâ (actually, Smalltalk borrowed the concept from a previous language, but we wonât go there now).
JavaScript was originally based on Lisp. In trying to keep the flavour of a âdynamic languageâ, JS does not insist on pre-defining all data structures before runtime. IMO, JS does this better than the Common Lisp, the Scheme, the Racket, the Clojure variants of Lisp. In Common Lisp, the designers chose to jump directly to premature-optimization using classes and built that concept in as syntactic baubles (âdefclassâ), that cause programmers to think in a Certain Way, even though the tools for less-calcified data structuring are still present (but, generally ignored by class-indoctrinated programmers).
Ideas calcified by compile-time optimizations cause programmers to think in Certain Ways and disallow other interesting forms of Creativity.
There IS another way to optimize - as seen in JIT and 1970's compiler technologies (e.g. gcc, OCG) and linking loaders. Optimize at runtime. Treat optimizers as barnacles attacking only already-working programs. Premature optimization has led us to building and using compiler-appeasement languages and has snipped off other creative avenues of thought.
Or, we could more simply divide programming into 2 camps (aka âdivide and conquerâ): (1) Design (2) Optimize. This division happens all of the time in more mature industries - products are released, and, only later, the products are cost-optimized. In fact, this division is so severe that it is given the name âProduction Engineeringâ. In contrast, Software uses the single term âEngineeringâ to mean âArchitectureâ and âEngineeringâ and âProduction Engineeringâ and âTest Engineeringâ and âDeployment Engineeringâ, and ...)
If you Design and Optimize all in one go, you are involved in a Cottage Industry. In a Cottage Industry, the same person, or a group, wears all of the hats.
Class-based inheritance is Waterfall Design. You must know everything about the data before you can write correct classes. Prototype inheritance can be iterative, you can change your mind later, you can incrementally alter the templates as you learn more about the problem space.
Compilers can be built to compile programs if the programs obey the rules of class-based inheritance. Compilers cannot, in general, compile programs that do not follow the rules of class-based inheritance, e.g. compilers cannot compile prototype-based inheritance, but, can compile class-based inheritance.
[conclusions]
I would argue that Self did not invent prototypal inheritance, but corralled the ideas and created the name. Selfâs contribution is the exploration of the space of data structuring and of making optimization be a continuum that can be applied at different times - not just at compile-time. This exploration ultimately led to the concept of JIT.
JavaScript was designed to allow âdynamicâ programming (whether it succeeded, can be argued) and was originally based on Lisp. Prototypal inheritance is less constraining than class-based inheritance, therefore, was made a part of the design of JavaScript.
To me âsystemâ means âdynamic languageâ. And âgeneral purpose programming languageâ means âstatic languageâ. Each kind is âgoodâ and each kind is âbadâ.
IMO, all big inventions in programming stem from the use of dynamic languages, and, ultimately from the use of assembler. GC, first-class-functions, Haskell, REPLs, etc., etc.
The only difference between class-based inheritance and prototype-based inheritance is when it happens. Both, class-based and prototype-based inheritance can structure data at compile time. In prototype inheritance, though, data can, also, be structured and re-structured at runtime. Runtime restructuring is not allowed in class-based inheritance.
Class-based inheritance is Waterfall Design. Protoype inheritance allows iterative design.
miscellaneous:
3 OO-ish data structuring techniques
1. classes
2. prototypes
3. mixins
2 is like 1, with some of the compile-time-only restrictions removed. The mixin idea, 3, looks like OOP, but is very different. In class-oriented OOP, you have a vector of operations associated with each class. In mixins, the reverse is true - you have a class without operations. Operations âdecideâ on-the-fly if they can be applied to a cross-product of parameters. All three methods get rid of explicit âif-then-elseâ based on type (which is the really big win - âif-then-elseâ is just bad). Mixins go beyond simple class-based inheritance - you can specialize operations on /value/ instead of type, you can create :before and :after methods. Capability depends on the method (âoperationâ) and not on the class. For a simple, nonsensical example, you can write a âplusâ method that works on {int x int} and another âplusâ method that works on {int x string} and another âplusâ method that works only on {nil x int}
⢠question to self: can you remove defclass from Lisp and leave only mixins? lisp already has deftype, why do you need defclass?
⢠lisp isnât a programming language, itâs a soup
⢠mixins ⣠assembler for constructing various kinds of class / prototype / whatever-based languages. I like to use the term âatomsâ when discussing fundamental building blocks. In my view, âmixinsâ are âatomsâ whereas classes and prototypes are âmoleculesâ constructed out of âatomsâ.