Just a bit more info comparing PrototypeBasedProgramming to typical class-based ObjectOrientedProgrammingLanguage''''''s: Rather than defining a "class" or a "prototype" from existing languages (and risking a debate over that), some basic object theory will be presented, and the notions of both a class and a prototype will be generated from that. These notions may not agree with what language X does; however it should probably be close to all of 'em - close enough for industrial work. :) If it ain't obvious, this isn't intended to be high scholarship. If you seen any errors, ''please'' fix them, but remember to JustCorrectDontPoint. Anyway, onwards... '''Our model''' The model of objects we will use comes from LucaCardelli and MartinAbadi's ''A TheoryOfObjects'' - a good book on the subject (though it assumes quite a bit of familiarity with the LambdaCalculus and CategoryTheory). In the book, an ''object'' is defined as a mutable unordered record of functions. Each function has with it an associated type, which we can write as follows: (T_0, T_1, ... T_''n-1'') -> R where T_0 through T_''n-1'' are the types of the input parameters, R is the type of the output parameter (we could view this as a classical function where there is only one input argument, which we are making into a tuple to handle multiple-argument functions. Also note that an empty tuple is a possibility). Note that these functions are (as far as we are concerned) ''pure'', there is no hidden reference to the object (no implicit "this"/"self" pointer). If we need one of those, we have to include it explicitly. Also note that ''attributes'' (data members) need no special handling; these can be modelled as functions which ignore their argument and return the attribute value. Finally, note that all functions in this model are ''replaceable'' - it is a valid operation on the object to replace a function with another with equivalent type, or to add a function. (Exceptions, including unimplemented functions, can be handled by returning BottomType). ''Note that modelling an object as a record of functions is only one possibility; it could instead by modelled by a single overloaded function which includes the attribute name as an argument. The type of an object then becomes an intersection of the individual function types, rather than a record type. This is arguably a more natural approach for prototype-based and actor-based languages, and in some respects it is simpler because it is not necessary for the function to be replaceable. (There is no loss of expressiveness because the environment of the function closure is still mutable, and this can be used to achieve the same effect as replacement.)'' [As a language developer, I find excluding a data definition as separate from a function definition in a class, to be very poor. How do you "store" a value to a function? It is obvious how you would store data to a variable that has a physical location in memory but for a function? What about the extra overhead of calling a function versus just accessing the data? To have a function that has a "type" implies that you have types of data so why not just specify that?] -- DavidClarkd '''Subtyping''' One point often made in OO literature is the distinction between ''subtyping'' and ''subclassing''. Subtyping is a relationship between two objects - one object ''A'' is a subtype of another object ''B'' if certain predicates hold. Subclassing is a mechanism for achieving subtyping. In our model, an object ''A'' is a ''subtype'' of another object ''B'' (''A <= B'') IfAndOnlyIf for each item in ''A'' there is a corresponding item in ''B'' with the same name and type. (Note that CoVariance/ContraVariance do not yet apply, as we are allowing functions in an object to be replaced with other functions). ''A'' may have additional members, in which case it is a ''strict subtype'' (''A < B''), or it may have the same structure as B in which case they are ''equivalent'' (''A = B''). Note that both the subtype and equivalence relationships are transitive; furthermore note that ''A <= B && B <= A'' implies that ''A = B''. This notion of subtyping is called ''structural subtyping''; it is supported by some functional languages such as HaskellLanguage and dynamically-typed OO languages such as SmalltalkLanguage. Many OO languages, including CeePlusPlus and JavaLanguage use a weaker notion called ''nominal subtyping'', where annotations in the code are required to declare a subtype relationship (the implementations of these languages ensure that a structural subtyping relationship exists between classes declared to have a subtype relationship). It should be noted that many consider nominal typing/subtyping to have its own benefits, as objects that are structurally similar may in fact represent different abstractions; the concept of nominal typing prevents a ComplexNumber from being passed off as a XyCoordinate; though both are composed of a pair of numbers. However, the same effect can be achieved using structural subtyping by including a uniquely-named attribute to represent properties of the abstraction not expressed by the other attribute types -- that is, structural subtyping can easily simulate nominal subtyping, but not vice-versa. '''Immutable members''' Before we continue, a useful thing to do is to consider that some members of an object may change over its lifetime; others may stay the same. If we can declare certain elements of an object as ''immutable'', this gives us several advantages: * Can make time and space optimizations (including eliminating storage for the immutable element entirely in some situations) * Can trap attempts to change an immutable element (on the assumption that this is a programming error) * If a field in an object is immutable, then the corresponding field in a subtype object can be a subtype of the base field's type, rather than being exactly the same type. ** For a function, this means return type CoVariance and argument ContraVariance. C++, SatherLanguage, and EiffelLanguage all support return type CoVariance; SatherLanguage supports argument ContraVariance. EiffelLanguage, strangely, supports argument CoVariance even though this makes the language not statically typesafe. Some would claim that contravariant argument types are not useful in practice, but they do get used in ObjectFunctional programming styles. It is generally agreed that covariant return types are useful. ** ''For the alternative model in which an object is modelled by a single function, contravariance is essential, since adding a method (or method overload) in that model corresponds to changing the function's argument type contravariantly.'' ''What do "immutable" or "read only" variables have to do with "Classes" or "Prototypes"?'' -- DavidClarkd '''Classes''' So far, we have dealt with types only as abstract concepts. However, in practical OO code, several patterns emerge: * Many programs will have large numbers of objects with identical structure (in other words, type-equivalent to each other). * Functions which represent ''data'' (zero-argument functions that return a fixed value) are frequently modified; whereas functions which represent "behavior" (those which have arguments and engage in computations of some sort, including (shh) SideEffects and external dependencies on the state of the program) generally are ''immutable'' - they are not modified over the duration of the object. (They may modify the object through a "self" reference, however) * Finally, the objects with identical structure frequently share the same "behavior" functions; in other words if two objects ''A'' and ''B'' are equivalent, there is a good chance that the behavior functions in each object with the same type will in fact have the same implementation. Given the above, an obvious optimization is the creation of entities which represent shared state between similar objects - ''classes''. The "behavior functions" (methods) of objects were moved out of the objects themselves, and placed in separate entities. In some languages, these entities were made full-fledged objects themselves; in others they were hidden from the programmer and given obscure names such as "vtables". With the addition of classes, a few other benefits arose: * Classes provide a handy way to construct objects * Classes provide a handy way of categorizing objects, and discovering their "types" at runtime. * Classes provide a handy way of dealing with shared state between objects (while not all languages implement this way; "static" data members can be viewed as data members of the class rather than of the objects themselves). Classes also make nominal typing possible - two objects are nominally equivalent if they have the same class. ''As pointed out above, uniquely named attributes can simulate nominal typing without using classes (this effectively substitutes the attribute name for the class name).'' While it wasn't necessary to do so (as Cardelli and Adabi point out), functions (other than the zero-argument type that we use to model data members) were banished from objects completely in most class-based OO languages. Most nowadays have ways around this via use of AbstractionInversion''''''s like FunctorObject''''''s (which make a function look like data suitable for stuffing in an object's data field); in addition C++ still allows function pointers to be non-static members of an object. ''FunctorObject''''''s would be an AbstractionInversion only if they were used in a language where functions are provided as a primitive concept. In languages where objects (or actors) are the primitive concept, FunctorObject''''''s are a perfectly natural approach. There is certainly no need to have both objects and (FirstClass) functions as primitive, since each can easily implement the other. This is not a workaround; it is just economy of mechanism.'' '''Delegation''' With classes, comes a fine example of delegation (one that many people aren't even aware of). When you invoke a function on an object; the call is really delegated to the ''class'' of the object (where the reference to the function really is). If you type the following in C++: foo->bar(1,2,3); The compiler turns this into something that looks like this (in C) foo->_vtable_->bar(foo,1,2,3); Obviously a very limited form of delegation, but that's what it is... :) '''Subclassing/inheritance''' With classes came the notion of ''subclassing''. Rather than require subtype definitions to match completely the set of members in a base class (and have to handle errors if they don't), OO languages made it easy - if you define one type to be a subtype of another, you get all of its members by default (including implementations). Furthermore, the corresponding class gets all the members of the base class. The subclass is allowed to redefine any of these that it wishes. Another popular name for subclassing is ''inheritance''; though arguably the term ''inheritance'' is more generic. Subclassing involves inheriting both interface (required for the subtype relationship to be valid) and implementation. You could, of course, have interface inheritance without implementation inheritance or the other way around (though the latter type of inheritance typically breaks the subtype relationship). Most class-based OO languages make you take the whole enchilada. '''Jettisoning the class - SelfLanguage''' Some programmers started to feel that the restrictions imposed by language implementations of classes were a BadThing. Returning to the fundamental object model introduced in ''A TheoryOfObjects'', the class was axed in SelfLanguage. The language, based on Smalltalk, was started by DavidUngar and RandallSmith at XeroxParc (see http://www.natbat.co.uk/self/selfHistory.php) and announced in 1987. The project later moved to SunMicrosystems, and the Self team was soon redirected towards working on Java. SelfLanguage languished for quite a few years (though Ungar is still working on it, and Sun seems to have cast it loose). SelfLanguage got rid of classes by replacing each of their functions with a completely different mechanism. So classes as object factories got replaced by copying of arbitrary objects, classes as inheritance got replaced with a completely general delegation mechanism, and classes as structural blueprints got replaced by maps. In part, this was necessary by the elimination of any distinction between data and code in Self; an objects' "slots" can hold either data or code, and calling the slot performs the appropriate function. (AnswerMe: How does CommonLispObjectSystem, which also uses "slots", compare to this?) ''CLOS doesn't have the concept of "member functions". Polymorphic (aka generic) functions are defined separately from types.'' Two other significant prototype-based languages followed - NewtonScript and JavaScript. The former has largely died out with the death of the AppleNewton (although the spirit lives on in IoLanguage); the latter is still cluttering people's desktops with annoying advertisements for Viagra as we speak. '''Whoops, structure isn't that bad - the birth of the prototype''' While classes as defined in traditional OO languages were limiting, it soon became apparent that the structure they provided is useful. Thus, the concept of a "prototype" is born. A prototype is similar to a class in that objects typically contain references to a prototype object, and can delegate operations to it. Unlike classes; objects in PrototypeBasedLanguage''''''s can override the functions defined in prototypes with their own implementations (doing this in a class-based language requires subclassing; though most allow ''anonymous classes'' to make this less painful). Prototypes fill other roles of classes - providing a hook for type queries, a classification system, and a way to create new objects. Many PrototypeBasedLanguage''''''s, such as JavaScript, even allow addition and deletion of class members at runtime (this is possible in a dynamically typed language; not in a pure statically-typed language). One other feature that is prominent in PrototypeBasedLanguage''''''s is ''delegation''. Rather than making the relationship between object and class implicit and ad-hoc; the delegation relationship is made explicit and more generally available. Some feel that delegation can replace inheritance completely; that is discussed on WhatIsDelegation and other pages. Interestingly enough, ''TheoryOfObjects'' theorizes a statically-typed PrototypeBasedLanguage. All PrototypeBasedLanguage''''''s in widespread use are dynamically-typed. '''So, classes or prototypes?''' The above is history and theory, and I hope that it's somewhat close to accurate. Now for some editorializing. The OO world seems to be witnessing a dichotomy between class-based and prototype-based languages (one of ''many'' dichotomies out there, it seems). This one generates less heat than StaticVsDynamicTyping (not to mention the language-X-vs-language-Y HolyWar''''''s), but it exists. To hear some tell it, the two ways of organizing objects are incompatible - you have classes, you have prototypes, but you got to choose. Hogwash, I say. Both methods of organizing an OO system or language have the same theoretical underpinnings. The biggest difference between the two (excluding things that are really orthogonal, such as JavaScript's ability to add/delete object fields on the fly) is that with classes, method implementations are immutable and can only be changed by subclassing; with prototypes they are all assumed to be mutable (and thus a bit more expensive to implement). C++ and Java both have good solutions at their fingertips with how they treat data elements. (This applies to some other OO languages as well). * ''The idea that "method implementations are immutable" is an implementation restriction imposed by some Java VM's. The question isn't really relevant to C++, because everything is immutable in C++ (it isn't an environment at all). In Smalltalk, surely a significant example of a class-based language, method implementations are readily changed at will. For those with courage, even the internals of an instance of Compiled''''''Method can be changed (the literal frame, for example). Some flavors of initialization/finalization are hacked this way, as are performance optimizations of proxy objects. Let's not confuse semantic differences between classes and prototypes by injecting implementation-specific restrictions.'' In C++, a data element can be declared ''const'', meaning that it can only be set by the constructor - once set it cannot be altered over the life of the object. (You can try and cheat with a const_cast, but if the results are UndefinedBehavior; you're on your own). JavaLanguage provides a similar capability with ''final'' members - once they are set in the constructor, they cannot be changed (Java doesn't allow casting away final, so things declared final really are). ''[Nitpick: the null value that a final field has before the object is fully initialized is sometimes observable.]'' This dichotomy can also be applied to methods. C++ supports const methods (and Java final methods), but both keywords mean something else when applied to a method (in C++, a const method is one which doesn't alter the object through the implicit ''this'' pointer; a final method in Java is one which cannot be overridden by a subclass). However, were either language to support (cleanly) methods ''whose implementation (though not signature) could be changed at runtime'' they would have much of the benefits of prototypes. C++, as mentioned above, can fake it with function pointers or FunctorObject''''''s (however the implicit passing of ''this'' does not occur; you have to pass in ''this'' explicitly). Many UI frameworks for C++ (Qt, for example) use a "slot" mechanism to handle the PublishSubscribeModel. Java can kindasorta fake it with pseudo-functors (objects with a single method with a well-known name like "eval"); the Pizza dialect of Java supports true first-class functions and thus can act like C++ in this regard. But there is no reason that a language cannot have the best of both worlds. (The same probably applies to many other HolyWar''''''s, if people would take off their blinders.) Unfortunately, there is no advantage to classes. There is no "best of both worlds" since prototypes subsume classes completely. The reverse is not true since class-based languages limit delegation to the inheritance hierarchy. Which is what a class-based language is ''by definition''. * ''In many ways, this is technically true--one could view a "class" as a collection of prototypes wherein certain slots (or whatever you wish to call 'em) are identical for each instance. On the other hand, this behavior is often what you want--the "class" remains a useful notion, even if viewed as SyntacticSugar for a common use case of prototypes. As pointed out above, many non-prototype OO language can fake prototypes with FunctorObject''''''s. The point of this page is to show that the two ideas really aren't that far apart, at least as not as far apart as some would have you think.'' * Perhaps I'm looking at a different page than you are but I see nothing here that describes how to fake prototypes using functors. And yes, you can fake prototypes using functors but that's because functors are lambdas and you can implement objects using lambdas. So really that's not at all interesting. Meanwhile, implementing classes on top of prototypes doesn't require any such AbstractionInversion''''''s. It merely requires that you place artificial restrictions on the language, lobotomizing it. This to me proves the superiority of prototypes. Equivalent but superior. ** To clarify the point above (it sounds like you are thinking of something else); use of "functors" in the above discussion refers to the practice of (in C++) arranging for a class to have data members whose type are FunctorObject''''''s and/or function pointers; thus allowing different functions/functors to be bound to different instances of the same class of object. NOT of throwing out the native object model and implementing objects with lambdas. The latter can be done, but its brain-dead. ''Thanks, I missed that.'' * The interesting question is whether classes (prototypes + maps) deserve their own special syntax. The answer is a flat out no. With true delegation, classes simply don't behave in any way differently from proxies. Further, state and predicate classes, enabled with true delegation, were always understood to be classes. Even multimethods actually belong to classes, though they can't be easily implemented in a class-based language. There is '''no''' useful distinction to be made and using syntax for artificial distinctions is an evil, evil thing. (Using similar syntax for two radically different things is also an evil, evil thing. Lisp's lambdas come to mind; I'm a partisan of ST's block syntax.) ** The only special or differing "syntax" involved is found at construction/declaration--using different "syntax" to ''declare'' two different things isn't evil, at least not in my opinion. (Having different syntax to ''do'' the same or similar things to two different entities is certainly a DesignSmell--I would certainly want the same notation to invoke a function on a prototype as on a class instance; I shouldn't have to care how an object was constructed to call its methods). I put "syntax" in quotes here simply because I am using a rather loose definition--meaning anything you type differently. Some people might see the word "syntax" and think that different rules in the grammar, or special/different tokens, etc. are being used. 'Tis not what I mean. ** ''I'm not likely to complain about loose usage of the term syntax since I've used it to refer to processes in an OS. :) My point however was that constructing a class' instance is no different from constructing a proxy under true delegation, and that what a class' instance does is no different from what a proxy does under true delegation. And under true delegation, classes are simply never declared. So there is ''no distinction'' and "classes" simply should not exist under true delegation ''even'' as syntactic sugar.'' What can you do with true delegation that you can't do with inheritance? Well, you can implement PatternMatching very easily. You can also junk the idea of PredicateClasses. But that's trivial. The real power of delegation comes in when creating wrappers. With delegation, 'self' always refers to the original object that received the message. In a prototype language that means the wrapper. So if a method in the wrapped object returns self to the sender, then that's ''the wrapper'' it returns. OTOH, in a class-based language, self would be the wrapped object itself. *I have an idea-how about we combine prototypes with interfaces? An interface is a handy summation of the signature of various methods, useful for typechecking pollymorphically. These are used in JavaLanguage and BooLanguage. *So we could make a 'class' syntactic sugar for making a kind (prototype used only for delegating clones) and an interface that matches it. *Best of both worlds--yey. '''Implementation in Self''' A quick look at how prototypes are implemented in Self will, I think, clarify some of this. All Self objects include a pointer to a structure which is very like a C++ vtable. The Self team calls them "maps". The map describes the object's layout and contains pointers to implementations for methods. When a Self object is modified by creating a new slot or method, it copies the map, adds the new slot, and updates the object to point to the new map. Any other objects which use the old map will continue to use the old map. This design allows objects with identical structure to share the same map. So, in a roundabout way, Self has something resembling classes hidden in the implementation and adding a new method to an object automatically creates a new class. But this view is moronic because it would mean that an object can ''dynamically change its class at runtime'', which is something quite a bit beyond the usual conception of class. And of course, the importance of burying an annoying language "feature" within its implementation can never be underestimated. For instance, dealing with file I/O versus TransparentPersistence. It is explained in detail in the paper ''AnEfficientImplementationOfSelf'' by Chambers, Ungar, and Lee (http://citeseer.ist.psu.edu/14611.html). ''This is virtually the same trick (hack?) that Digitalk put into their Smalltalk just before they got absorbed by ParcPlace. At the time, I think they called it "Instance-specific behavior". I think one way to explore the relationship between classes and prototypes might be to ask "What is the difference between a prototype and a class with one instance?"'' Well, in some languages (Smalltalk, Ruby, Python, et al) classes are objects. In other languages (C++, Java) classes are nothing at all like objects. I think in languages where classes are objects, the difference between classes and prototypes is barely there. It seems to me that one could easily implement Smalltalk on a Self VM, and likewise, one could easily implement Self on a Smalltalk VM. ''True enough. I think its a bit more easy to do Smalltalk on a Self VM, rather than vice-versa, because of the intimate knowledge the Smalltalk VM has about classes and methods. One of the strongest and most interesting arguments in favor of Self, when it first arrived, was the elegance with which it supports class-based programming styles.'' ''Yes, classes are objects; but that doesn't remove the distinction, because objects aren't classes, and classes can't do everything you need to do to use objects.'' Indeed there was actually an implementation of Smalltalk on top of Self that at the time actually out-performed most other available Smalltalks (due to sophisticated Self VM trickery which was later adopted into the HotSpot VM I believe). ''Are you thinking of PolymorphicInlineCaches?'' ---- See LearningFromPrototypes for an idea about giving classes the power of prototypes. Relevant article: http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.html (comparing both ways, favors prototypes in conclusion) ---- CategoryComparisons CategoryPrototypeProgramming