Polymorphism in RSL

russell, 17 june 1998

RSL treats objects according to their actual class, rather than what class they are perceived to have. This is a direct result of the fact that in RSL, objects interpret the messages they are sent, rather than the philosophy taken by less dynamic languages such as C++ and Java, where member functions are called with an implicit context (the context being the "object"). In C++ and Java, the class an object is perceived to have is more important that what class it actually is; the very existence of the need to cast is evidence of the fact that we increasingly need the ability to determine an object's class at runtime. C++ lacks the foundation to do this, and is the most obtuse in this area. Java, while better, is still very lacking. We can do it in C++ and Java, at some level, but it is not an intrinsic feature of the language, making it difficult to use.

Consider the following example of what is sometimes termed "downcasting". Given an object of some very abstract superclass like "Object", we want to use it in ways depending on what class it actually is. Clearly we can't arbitrarily change the superclass to account for all the various things we might want to do with it and let the subclasses vary their implementations (the "virtual function" approach). This situation exposes the static natures of C++ and Java, despite "run time type identification" in C++ and various reflective abilities of Java.

I have partially implemented a simple class in the three languages below. What I want do is tell it to "route" with some object, and have it take a different action according to the actual class of the object.

lang class solution: some method or function trying to use x.
Java
public class x {
	public void route(String s) {
		// ...		
	}
	public void route(Integer i) {
		// ...		
	}
	public void route(Object o) {
		// ...		
	}
}
void f(Object o)
{
	x thex = new x();

	if (o instanceof String)
		thex.route((String) o)
	else
		if (o instanceof Integer)
			thex.route((Integer) i)
		else
			thex.route(o);
}
C++
// assume the existence of a class
// heirarchy Obj <- {S, I}, ie,
// S and I are subclasses of Obj.
class x {
  public:
	void route(S *s) {
		// ...		
	}
	
	void route(I *i) {
		// ...		
	}
	
 	void route(Obj *o) {
		// ...		
	}
};
void f(Obj *o)
{
	x thex;
	S *s = dynamic_cast<S *>(o);

	if (s)
		thex.route(s);
	else {
		I* i = dynamic_cast<I *>(o);
		if (i)
			thex.route(i);
		else
			thex.route(o);
	}
}
RSL
class x {
	route(String s) {
		// ...
	}

	route(Integer i) {
		// ...
	}

	route(Object o) {
		// ...
	}
}
f (Object o)
{
	x thex;
	x.route(o);
}

In both C++ and Java, the type of the object must be explicitly checked by the programmer, and the object must be cast. This is because the compiler must do type checking at compile time.

The RSL solution is simple and easy to understand. Depending on the actual class of the object o, the specific version of route is invoked by the object thex. It works because RSL knows at runtime exactly the class of the object, whether the programmer knows it or not. Sending the message route to thex switches appropriately on the class of the object o, no matter what class it is perceived to have in the code itself.