General musings on programming languages, and Java.

Sunday, March 02, 2008

Implementing OOP in Java

Object-Oriented Programming is all about late binding. Java has some support for it via polymorphism, but it is not as late as it could be. Java makes some attempt to guarantee things statically, e.g., method calls are guaranteed to correspond to an actual method at runtime. This has a couple of implications:

1. Some of the dynamism that languages like Smalltalk, Python, Ruby and Groovy have is lost.

2. In attempting to reach that dynamism, programmers end up doing things like: throw new UnsupportedOperationException, because their interfaces are too big, and having smaller interfaces would increase the number, and hence perceived complexity, of APIs (yes, this really is the reason java.util has 'optional methods' in interfaces).

Static typing, particularly when it's done really well, is incredibly useful. So useful that it can replace lots of unit tests, or even make unit testing even easier to write. It's actually possible to guarantee that code cannot have a runtime error, if you write it with that in mind. However, some code really needs to be dynamic, at least temporarily, because it's being written by an amateur, or needs to be swapped in or out at runtime, or is an idea that might not match up with your language's typesystem just yet. In that kind of code, guaranteeing that there are no errors is actually an anti-goal.

So, let's implement this idea of OOP, in Java. Some languages have a symbol type that is quite appropriate for this, but Java doesn't, so we'll use strings. To avoid annoying namespace collisions, we'll call our Object type Dyn, short for Dynamic. I chose this short name to not distract from the meaning of Dyn-using code. A Dyn is an object that has a method, ap, that takes a String and zero or more Dyns as parameters, and returns a Dyn. (Some conversion methods to Java's static types might be useful too)

public abstract class Dyn
{
    public abstract Dyn ap(String name,Dyn... args);
}
And an implementation, just enough to try it out:
public static final Dyn identity=new Dyn()
{
    public Dyn ap(String name,Dyn... args)
    {
        return this;
    }
};
The above Dyn is a bit silly, whatever message you send to it, it returns itself, and that's all. Here's one that holds a Java int and when the message "sqr" is passed to it, returns a Dyn holding the square of that int:
public static final Dyn squarer(final int i)
{
    return new Dyn()
    {
        public Dyn ap(String name,Dyn... args)
        {
            return name.equals("sqr") ? squarer(i*i) : identity;
        }
    };
}
Here I'm using the identity object to represent an error/missing method, which seems broken, but right now we can't actually observe that because there's no way to print a Dyn or convert it back to a Java object, so it doesn't really matter. I could add a toString() to the implementation to be able to see the results. I could write Rails for it, with all the method_missing goodness involved.

My question to you who have read this far is, if Dyn was made more a part of a static language instead of a clumsily-added library as shown here, would you be tempted away from Ruby et al? Let's, for this post, reserve the [] brackets to say 'in here be dynamic code'; any time we don't want the typechecker to look at our code, we put it in there, so we might write something like: Animal animal=new Dog(); [ animal.woof(); ] You could write all your code between [ and ], and gradually move it outside when you know how to make it type check, hence incrementally getting better reliability and performance (but reduced flexibility).

Note that this isn't the same thing as adding explicit static types to a dynamic program. Good static typing doesn't need explicit types everywhere, thanks to type inference.

Would this be an attractive option? Does such a language already exist?

Blog archive

About Me

A salsa dancing, DJing programmer from Manchester, England.