Rick Denatale wrote:
But that’s just me, after all it’s all just images caused by smoke and mirrors
Perhaps. But there’s also the more mundane possibility that it’s
just inconsistent semantics and bad terminology.
There seems to be two forces contributing to this. Firstly, the
profusion of terms (many of which are clearly misnomers) is getting out
of hand, and is unhelpful to newcomers. Secondly, many people still
resort to referring to MRI runtime structures when discussing semantics
even though Ruby is now being implemented on the JVM, on the CLR, on the
Objective-C runtime and presumably elsewhere. There is now a clear need
to be able to discuss semantics at a sufficient level of abstraction,
i.e. purely in terms of what is revealed by the reflection API. If there
are any inconsistencies or anomalies revealed by the API, then this
should be addressed directly.
The central point that I think should be made here is that per-instance
behaviour has nothing to do with inheritance, per se. One obvious
language rule will be that per-instance behaviour should take precedence
over instance-common behaviour and one convenient way of implementing
that is to snap an eigenclass into the chain between the instance and
its class. But that implementation-specific runtime structure won’t
serve as a definition of the semantics for implementations that work
differently. Take, for example, a “Ruby in the browser” (perhaps one
already exists?) that translates onto the JavaScript object model. It
would make perfect sense for this to implement per-instance behaviour as
object properties. The class << obj syntax would then just be a way of
manipulating the per-instance behaviour collectively. The only
assumption being that method resolution is correctly (and hopefully
efficiently) implemented. And the eigenclass would be purely a
conceptual notion, existing only as several properties of an object,
taken together. No problem whatsoever.
It seems that the snapping of eigenclasses into the structure was
intended to be a private implementation detail. Certain reflective
methods were written in such a way as to skip over the eigenclasses, as
though they were not to be regarded as part of the public (semantic)
inheritance hierarchy. However, there were one or two leaks. For
example, eigenclass.superclass reports the original class of the
instance (in 1.9 at least). And, in a similar vein, the keyword super
chains from an instance-specific method to a regular instance method.
(As it happens, it transpires that this is convenient way of enabling
interception.) Ideally, one would like to stem the leaks as far as
possible without causing too much breakage of existing code.
Here’s a few conclusions I’ve come to. I’d be interested to hear what
others think.
(i) The correct way to present method resolution is the way
Flanagan/Matz do on page 258 of their book, i.e. as an algorithm. It’s
simple, intuitive and easy to remember. Ruby implementations are free to
implement this algorithm any way that’s convenient (and fast) in their
given context. The wrong way to present method resolution is the Pickaxe
approach of using diagrams from MRI’s runtime structure. Apart from not
being portable (which will become a bigger and bigger problem), it’s
backwards. That runtime structure exists like that in order to support
certain desired semantics. So deriving the semantics from the structure
is neither here nor there. Just state them up front.
(ii) The keyword super should just be defined to work according to the
method resolution algorithm, not the inheritance hierarchy. This is what
happens at the moment, so it’s just a formalising of the de facto
situation and doesn’t break anything. This would preserves the
convenient chaining from a per-instance method to the regular instance
method and makes it clear that there are potentially lots of other
useful chaining opportunities. As Gary pointed out to David earlier in
this thread, the sequence of classes produced by the method resolution
algorithm is different from the inheritance hierarchy because of module
mixin and eigenclasses. (And in Ruby, module mixin is not inheritance,
regardless of the similarity.)
(iii) The superclass method should be defined to return Object for the
eigenclass of a regular object. In other words, the original statement I
alluded to from page 261 of RPL would now be true. The current situation
(returning the class of the instance) is an implementation leak. I don’t
think there would be much breakage resulting from this and it cleans
things up.
(iv) In principle, class method resolution could also be defined purely
by an algorithm, as a language rule, which would then not require
explicit semantic inheritance to work.
Lastly, the terminology situation needs to be addressed by the
community. The term eigenclass is really spot on for a conceptual
container for per-instance behaviour and it’s great that RPL adopted it.
The other terms are not acceptable synonyms; they are misnomers.
(a) metaclass
Even _why, in the 2005 piece that led to the widespread use of the term
metaclass, acknowledged that the term was wrong: ‘This definition
doesn’t really work with Ruby, though, since “a class which defines a
class” is simply: a Class’. The simplest way to illustrate this is to
compare Smalltalk and Ruby. In Smalltalk, if you evaluate the expression
“Integer class”, you get “Integer class” (i.e. the same thing). The
answer being the same as the question is an expression of the anonymous
nature of the metaclass, but is read by a Smalltalk programmer as “the
metaclass of Integer”. In Ruby, if you evaluate the expression
“Integer.class”, you get “Class”. Thus, even if the eigenclasses of
class objects in Ruby play a similar role to metaclasses in Smalltalk,
they are not metaclasses. By definition.
(b) singleton class
As pointed out in the “Ruby in the browser” example above, there is no
need for the eigenclass to actually exist as a regular class, let alone
have a single instance. Clearly then, the term singleton class would
only be appropriate to some implementations and would consequently be a
misnomer.
While I’m at it, the term “singleton method” is also a misnomer. The
term “singleton” derives from set theory. A singleton set is a set with
one element. As such, the GoF pattern is a perfect fit. However, there
is no singleton set anywhere in this situation (unless you get really
silly in your choice of set). You can have many instances, each with
many per-instance methods. The only appearance of the word “one” is on
one side of the cardinality of an association (and not on the method
side). For reasons of symmetry, eigenmethod is a far better term.
Regards,
Danny.