(warning: long)
I’ve been following the multi-methods thread closely and having
recently completed the book “The Art of the Meta-Object Protocol” by
Kiczales et al. (matz Must have read this book, it’s so ruby-ish (and
ruby is so MOP-ish)), so I’ve got multi-methods on the brain.
My infatuation aside, I’ve been following everyone’s arguments and my
opinion is that multi-methods may not be needed in Ruby due to Ruby’s
special ability to redefine classes on the fly.
Some background:
I see overloading/multi-methods/paramater typing as useful in those
cases where we want to multiply, over a given operation, one set of
types by another.
For example, we might have some graphical objects and a Screen or a
Printer on which to draw them. So that would be multiplying over the
print operation:
{ Screen, Printer } print { Circle, Triangle, Square }
Or, perhaps the normal Xml printing problem over the toXmlString
operation:
{ XmlSerializer,
RdfSerializer } toXmlString { DataNode,
OpNode,
TypeNode }
Or, various execution contexts over execute on various command
types:
{ ImmediateExecutor,
QueuedExecutor,
SecureExectutor } execute { ReadOnlyCommand,
TransactionalCommand,
TaintedCommand }
So my question for myself and everyone else is: Where does this
multiplied -between- logic belong in Ruby?
In Other Languages, (Java, C++, CLOS), it belongs in the class itself
and the dispatch decision among the various overloaded methods is made
on the TYPE of the given arguments.
Java:
class XmlSerializer {
public void serialize( DataNode n ) { /* do stuff to DataNode / }
public void serialize( OpNode n ) { / do stuff to OpNode / }
public void serialize( TypeNode n ) { / do stuff to TypeNode */ }
}
In CLOS, quite differently, it belongs -between- the class
definitions, but the decision is still based on type. To me, this is
appropriate (just ask the AspectOriented folks) but this style still
requires typed parameter lists and multi-methods…
;;you don’t really need this per-se since all the logic is in the
;;serialize methods, but let’s pretend we’ve got a bunch of possible
;;serializer types
(defclass xml-serializer ()
())
(defgeneric serialize (stream node))
(defmethod serialize ((stream xml-serializer) (node data-node))
;;data node stuff)
(defmethod serialize ((stream xml-serializer) (node type-node))
;;type node stuff)
(defmethod serialize ((stream xml-serializer) (node op-node))
;;op node stuff)
So, in Ruby, where does this -between- stuff currently belong?
You could do the dispatch yourself inside one (or each) of the classes
on one side of the multiplication (ala Java):
class XmlSerializer
def serialize( node )
case node
when TypeNode:
serializeTypeNode( node )
when DataNode:
serializeDataNode( node )
end
end
end
Or you could put it inside one (or each) of the classes on one side,
and use an idiom that I think captures the -between-ness in a uniquely
Ruby way (akin to DoubleDispatch):
class XmlSerializer #this is the ‘owner’ of this -between- logic
#the key is to define specialized operations on all of the
#intended partner types
class << TypeNode
def between-XmlSerializer-Nodes-serialize( serializer )
# do TypeNode stuff
end
end
class << DataNode
def between-XmlSerializer-Nodes-serialize( serializer )
# do DataNode stuff
end
end
class << OpNode
def between-XmlSerializer-Nodes-serialize( serializer )
# do OpNode stuff
end
end
# and now, the main method
def serialize( node )
node.between-XmlSerializer-Nodes-serialize( self )
end
end
This method has a few stengths:
-
easy to understand
-
you circumvent overloading/multi-methods (NOTE: you can define
-between- methods as high-up in the target class heirarchy as you
like, for example on the generic Node class)
-
requires no changs to ruby
-
captures the ‘cross-cutting’ nature of the AspectOriented ideas
(modularizes the concern)
-
namespaces the methods in the target classes to prevent collisions
Is this the extent of the usefullness of overloading/multi-methods or
do we need more?
Also, is this too weird? We are changing other classes’ definition’s
without their consent, but it does seem Ruby-ish to me…
-Rob
···
–
Thus far we seem to be worse off than before - for we can enormously
extend the record; yet even in its present bulk we can hardly consult
it. --Vannevar Bush, 1945