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?

5 comments:

mateusz.fiołka said...

1 April. Too late.

Jacob Gabrielson said...

Some implementations of Common Lisp do something similar. You can start with this:

(defun foo (bar)
...use bar...
)

And later add a "static" type:

(defun foo (bar)
(declare (string bar))
...use bar...
)

Regardless, the compiler will also have attempted to do type inference and will usually warn you when it can't figure the type out (and/or it seems to conflict with what you are declaring). In the following contrived example we try to use '+' on a string. Note that the compiler has figured out that 'qux' is a string even though only 'bar' has the declaration:

(defun foo (bar)
(declare (string bar))
(let ((qux bar))
(+ qux 23))) ; SBCL warns about use of '+' here

You can get pretty specific, such as:

(defun snord (bar)
(declare ((integer 0 13) bar))
(format t "~A~%" bar))

(snord 14) ; compiler will error out here

Unknown said...

Interesting idea. I have done something similar where [] equates to a groovy class/method on a couple of Java projects lately. I have found it useful in situations dealing with external defined APIs (something like WSDL definitions for SOAP - not that I condone that sort of behavior). I assume this is the kind of scenario you mean by swapping in or out at runtime.

If you look at how you would publish or consume a web-service in ruby, compared to the rubbish generated code you are faced with in Java, you can see the potential benefits for defining some form of inline dynamic behaviors.

I guess the difficult part of implementing something like this is ensuring you get the best and not the worst (or at least average) of both sides - kind of like the optional types in dynamic languages can be. Without a clear separation between the two approaches you risk losing some of the power. Of course these are just implementation concerns :)

Unknown said...

Sounds a bit like Objective-C to me -- your example above would become:

Animal *animal = [Dog new];
[animal woof];

If the animal object doesn't support the woof object, the call does nothing by default.

Later on, you can formalize the woof call by adding it to the interface for Animal, and the compiler will give you the benefits of static typing as well.

I'm sure I'm glossing over some of the details, but you should take a look at ObjC anyway.

Anonymous said...

If I understand your point correctly, you're searching for a language that lets you develop in a dynamic type system and later convert your program into a statically typed one to ensure it does what it's supposed to.

Optional static typing is definitely possible in Objective-C. It works like this: Whenever an object reference is statically typed as "id", you may call any method on it. When it's typed using a class name or using a protocol (in Java: interface) or both, you may only call methods that are supported. However, when calling the -woof method on a reference typed as Animal*, this should result in a compiler error IIRC.

I've never seen anyone write code in a mainly dynamic fashion in ObjC. ObjC lacked static typing in the past, but since it's there, noone seems to have written any code that doesn't use it... Admittedly, I believe this is partly due to a lack of support of proper tools in the (GNUstep) environment where this code was developed.

As a side note, I recently listened to a german podcast episode on Lisp (Chaosradio Express), where the Dylan language, a Lisp derivate with bracket-avoiding syntax, was mentioned as a language also providing optional static typing.

I wonder what happens when you apply type inference on the existing code in, say, a Smalltalk-80 like system. Will the conventional Java-like type system be enough to ensure type safety there? If not, what will be the extensions required in the type system?

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.