Saturday, July 21, 2007

Array-like objects in prototype-based JavaScript

In the course of a recent exchange on what constitutes prototype-based as opposed to class-based in OOP, I took a moment to look at a few neo-JavaScript interpreters.

An array-like object is an object which satisfies some part of the interface which an Array prototype would specify. First create an object obj = {} and then add to the object with obj[0] = 1 and finally test the object for type, length, constructor and prototype.

All of the interpreters respond that obj is of type Object. One reports that obj now has a length of 1 with no constructor and Object as prototype. The others responded that length was undefined with no prototype but a constructor of type Object.

The downside of this little exploration has been a re-reading of Chapter 7 on OOP in Peter Van Roy's CTM. My estimation of that otherwise fine book has suffered.

It seems that the first thing that goes wrong in Chapter 7 is the good intentions.
One good intention is to warn you that object-purity is not the answer and another that class inheritance is not the answer if the subclasses violate the type of the superclass. If type offends you, then substitute "fails to respect an invariant of a superclass". And in passing, if you have multiple inheritance, only inherit from unrelated classes.

Anytime inheritance trumps composition or runs deep where it need not do so, a design suffers and maintenance and evolution become problematic. Classes for a GUI framework where inheritance may naturally run deep can be the worst place to learn OOP. Ditto for collections and containers. The ease of subclassing is the undoing of many projects. And the ease of extending base classes. And failing to respect a separation of concerns.

However, Class itself typically has only one subclass: Metaclass and Class itself likely lies only 3 or 4 classes deep in the Object hierarchy.

Peter Van Roy fails by misconstruing object-oriented as class-based and ignoring prototype-based. Had the author given any attention to Self, JavaScript or Logtalk, this could not have happened. Instead we have Smalltalk-80 and Java with a very short note on object-based. There is nothing about "add-on" OOP although it is common as rain in Prolog itself (perhaps because it was such a vice in C++.) When it comes to his reports of failures, just remember all of those industrial-strength Prolog apps which have been re-written by imperative programmers. A grain of salt is in order.

The use of Smaltalk-80 is problematic because Smalltalk has no ISO standard as does Prolog. What Smalltalk has are implementations. Had the author looked at VisualAge Smalltalk he could have cited a plethora of types: IBM Smalltalk came with 5 types of subclasses. Not to mention that a subclass of nil ( Object and any light-weight objects which serve as proxies) form at least two types as does any object implementing >>doesNotUnderstand. You could argue the same for classes with class instance variables as opposed to those with only instance variables or class variables. Smalltalk has runtime dynamic type-resolution and so is not strongly-typed in the sense of the compile-time type resolution of many other languages. An untyped language would not be more powerful than the alternatives: it is in theory more restricted, being the language with one implicit type, the 'any' type or the 'just-cannot-say-yet' type. It certainly could not easily implement the power of multiple value-side datatypes for single-type variables.

So one suspects that the consideration of Smalltalk-80 is consideration of a strawman. Indeed the dangers of subclassing for inheritance are presented as the dangers of side-effects and an opportunity is missed.

The object-orientation that is presented is a minimalist class-based approach with no method overloading within a class. This should have struck me as absurd in a language inspired by Prolog.

One suspects that the target is the unnamed sin of C++. But Objective-C is ignored, as is Lua and embedded scripting in general. Java is given the most attention and C# is ignored, itself a regrettable choice.

That the entire book ignores JavaScript may be a symptom of ignoring scripting. But this is a book on techniques and not just algorithms: Concepts, Techniques and Models.

The warning against 'structure' versus 'type' inheritance is just hollow. Substitute 'module' for 'class' and 'dependency' for 'inheritance' and try to convince yourself of this in a book that stresses the importance of modules and in light of the importance of modules in Prolog and imperative languages. The reader would be better served to look at class-type declarations that permit 'mixed' and 'convenience' modules to be constructed ( something more awkward in languages where module equates to physical file directory ). Of course what is a directory, but just the kind of unprincipaled 'structure' which the author deplores. One might as well deplore the use of 'path' in navigating these structures as 'path' has an equally unprincipaled order (but putting '.' at the head of classpath is deplorable.)

What is curious is that Van Roy ignores significant similarities between Oz and Smalltalk in the area of overloading methods within a class. Smalltalk with optional Types, Strongtalk, does provide overloading. The need for overloading in Smalltalk is revealed by counting calls to >>class or indexing into the Smalltalk SystemDictionary with symbols or calls to >>isKindOf: It is an aspect of polymorphism that should not be ignored. It is present in typed Prolog.

If MIT Press goes for a second edition of CTM, here is hoping that this entire chapter gets a re-write ( including the tell-tale editing error which seems to reveal that some material in the chapter was destined for another location in the book.) By that time it may be the JVM and .NET who deserve attention if Scala develops a following. A book with chapters on declarative and OOP cannot plausibly ignore Logtalk.

Objects alone are not the answer for distributed or concurrent or collaborative computing. Nor is class-based object orientation. But objects, whether referred to as records or associative arrays or hashtables are part of the answer wherever functions are first-class objects.

Oz is just too important to the case for multi-paradigm programming to have its only book and such a significant book marred by such a treatment of object-orientation. Had the author gotten past Smalltalk-80 to Self and JavaScript, the objectives of CTM would have been better served. As it stands, the book ignored distinctions between Java4 and Java5 which cannot be ignored as we move to Java6. If ECMAScript leaves prototype-based and Rebol3 leaves prototype-based it will be all the more important to assess the extent to which class-based is the answer for effective object-orientation.

A chapter on Logtalk could be the framework on which to hang the trio: Concepts, Techniques and Models. There is no need to hang the strawman.
PS
Oz itself has been called 'concurrency-oriented'; nothing prevented Oz from being prototype-based as was Rebol (prior to Rebol3) and with the flexibility of Logtalk+Prolog.
For discussions see Crockford and OZ Classes on principal

1 comment:

KanjiRecog said...

Here is an example from mature class-based OOP for Prolog, in this case PDC Visual Prolog with
overloaded class predicates
(ex. taken from \pfc\vpi\vpiToolbar\vpiToolbar.pro)

tooltip_change_if_not_current : (vpiDomains::windowHandle) determ (i).
tooltip_change_if_not_current : (vpiDomains::windowHandle TbWin, vpiDomains::rct ControlRCT, string Tip, vpiDomains::pnt) determ (i,i,i,i).
tooltip_check_first_time : () procedure ().
tooltip_check_first_time : (vpiDomains::windowHandle) determ (i).

PDC is not at all unlike Mercury, so in no way unafamiliar in the land of Oz. It is, unlike Mercury, a commercial product with a 'personal' version for free download. Mercury is open-source like Oz.

PDC is not as flexible as Logtalk with any of 20 Prolog implementations as Logtalk permits prototype-based to mix with other OOP styles with or without classes and inheritance. The degree of reflection is determined by the framework employed. Amzi! Prolog, which is now an Eclipse-hosted Prolog, also had its own OOP at the time CTM was written as did LPA Prolog and SICStus.