Correcting the following posts, because I should be very clear about such a controversial statement. Prototype inheritance does not cause the same problem as virtual methods, because the prototype is only accessed if the method does not exist in the object being called on. Also, as far as I know, prototypes are usually only applicable to non-type safe (dynamically or duck typed) languages.
The problem with virtual method inheritance occurs only with type safe languages, where virtual dispatch violates the the *SEMANTICS* (behavior) implied by the type safety. It is a dynamic typing paradigm. For example, if CBag derives from CSet, but the virtual put() method in CBag allows duplicates that CSet does not, then a reference to CSet which is really a CBag instance, will violate the expected semantics of the type safe check (i.e. if CSet is an argument to a function). Another example with Rectangles and Squares: http://en.wikipedia.org/w/index.php?title=Liskov_substitution_principle&oldid=337693896#A_typical_violation The relevance of this to Neko is somewhat interesting because HaXe targets Neko and offers type safety. Perhaps it is more relevant to HaXe, except I mentioned here in context of HLLs (some may support virtual methods) interoperability through Neko common VM. The above are examples of the general axiom of "Liskov Substitution Principle" that it is an undecidable problem that subsets inherit. The problem can be more generally classified as one of conflation of the granularity of inheritance. Essentially CBag and Square inherit some aspects of CSet and Rectangle, but not all. In order to support the differences, the sub-class typing is broken by a dynamic typing virtual method (broken in the cases that one desires type safety). In order to support type safety correctly at the necessary granularity, then correct OOP is to make the inheritance more granular via interfaces (which can be composed when making new classes). The implementations of interfaces can even be inherited granularly with a single declaration, with for example "mixins" in Python, instead of a "all or nothing" class inheritance. I explained in a prior post that type safety is important when reasoning about public interfaces that can be used for infinite combinations by external users: http://lists.motion-twin.com/pipermail/neko/2010-January/002705.html And that reasoning is further explained in a discussion about duck typing: http://en.wikipedia.org/w/index.php?title=Duck_typing&oldid=339677020#Criticism More discussion of trade-offs: http://en.wikipedia.org/w/index.php?title=Type_system&oldid=337013594#Static_and_dynamic_type_checking_in_practice I have agreed that for internal implementations (not public interfaces), where complexity of interoperability is known, then dynamic typing may provide benefits. Thus, I have made the point that for HLLs public interfaces interoperability we need simple type safe composeable public interfaces, without virtual methods or other esoteric OOP features: http://lists.motion-twin.com/pipermail/neko/2010-January/002713.html I have not studied the implications of "mixins" for cross-language interoperability yet. Complexity of inheritance and the desireability of composition (e.g. composing granular interfaces) is further explained here: http://en.wikipedia.org/w/index.php?title=Yo-yo_problem&oldid=320280265 Note that I have not claimed that strict typing solves LSP (granularity does only to the point more granularity is needed to avoid a composeability deadlock/conflict), thus nor have I claimed that dynamic typing or the existence of virtual methods (a limited form of dynamic typing) is undesireable in all cases. I am claiming that at the layer for (potentially infinite variations of use of) public interoperability of interfaces, then strict typing is superior to proliferation of runtime checks and exceptions. Public interfaces should be designed to be maximally granular and orthogonal, so that re-factoring is not needed (often). In other words, a lot of time and effort should be put into public interfaces, unlike for internal implementations where rapid prototyping and ability to solve complex problems in short time is often the overriding priority. I could have done a much better job of discussing this stance in past: http://lambda-the-ultimate.org/node/1277#comment-51719 =====================corrected posts========================== http://lists.motion-twin.com/pipermail/neko/2010-January/002703.html > I explained in my prior posts why virtual/prototype/base class inheritance > breaks "Liskov Substition Principle" and is a semantic obfuscation "can of > worms", and should be replaced by orthogonal+composeable interface > declarations: > > http://lists.motion-twin.com/pipermail/neko/2010-January/002691.html > http://www.haskell.org/pipermail/haskell-cafe/2009-November/068432.html http://lists.motion-twin.com/pipermail/neko/2010-January/002705.html > Virtual/prototype/base classes break "Liskov Substition Principle" and > thus break large-scale interoperability and composeability. I explained > that in the post you are replying to. If you read the links I provided in > my prior post, you see the example where one codes a reference to a CSet > interface as seen with the eyeballs in the code, but gets a CBag semantics > at runtime. It becomes impossible to reason about interoperability and > composeability over infinite scale of mashups, when one can't declare the > interfaces one will be operating on. http://lists.motion-twin.com/pipermail/neko/2010-January/002691.html > From my research below, polymorphism via interfaces (i.e. > orthogonal+composable, no run-time obfuscation of semantics via virtual or > prototype pointers) is superior to virtual or prototype inheritance -- Neko : One VM to run them all (http://nekovm.org)
