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)

Reply via email to