torstai 18. joulukuuta 2008

Static Checking is Defensive Programming

(But not vice versa.)

void openFridge(Object object) {
____if(!(object instanceof Fridge)) {
________throw new IllegalArgumentException("!(object instanceof Fridge)");
____} else {
________((Fridge) object).open();
____}
}


void openFridge(Fridge fridge) {
____if(fridge == null) {
________throw new IllegalArgumentException("fridge == null");
____} else {
________fridge.open();
____}
}

perjantai 13. kesäkuuta 2008

Reuse as a Refactoring Strategy

There has been a strange pattern reoccuring in a lot of my development efforts. Often, when I write a component X acting as a backend ("server"), and a component Y acting as a frontend ("client") to X, a very good way to refactor the code is to write another component Z also acting as a frontend to X. You start to notice redundancies between the frontends Y and Z, so you move the redundancy towards the backend X: the dividing line between the responsibilities of the frontend and the backend becomes clearer.

In retrospect, this seems obvious: the best way to write reusable code is to reuse it. However, more paradoxically, I believe such deliberate reuse can be used as a refactoring strategy even if you never plan to use Z.

I wonder if this idea already has a name.

tiistai 27. toukokuuta 2008

Inferential Duck Typing, or: Type Hierarchies Considered Harmful

Let's begin with some Java code:

interface File {
    void open();
    void close();
    void delete();
}

interface Fridge {
    void open();
    void close();
    void addSticker(Sticker s);
}

class Util {
    // Too bad this version only works for Files, not Fridges.
      
    void with(File file, Runnable r) {
        try {
            file.open();
            r.run();
        } finally {
            file.close();
        }
    }
}


How can we make Util.with work with both Files and Fridges? Obviously, by crafting an interface IOpenClose and making both File and Fridge extend it:

interface IOpenClose {
    void open();
    void close();
}

interface File extends IOpenClose {
    void delete();
}

interface Fridge extends IOpenClose {
    void addSticker(Sticker s);
}

class Util {
    // This works for both Fridges and Files.
      
    void with(IOpenClose obj, Runnable r) {
        try {
            obj.open();
            r.run();
        } finally {
            obj.close();
        }
    }
}


Unfortunately, the above version requires us to go through all code and mark all open-closeable classes and interfaces with IOpenClose. This is simply superfluous: everyone with two eyes can see that both Fridges and Files provide open and close methods, so why should this be marked with an additional hierarchy parent (IOpenClose)? Not only humans can see this, but a compiler can automatize it.

Here's what I propose: the language should provide implicit downcasting between classes and interfaces with compatible methods. Perhaps this shouldn't be the default behavior, but at least it should be possible. Here's an example:

interface IOpenClose {
    void open();
    void close();
}

interface File {
    void open();
    void close();
    void delete();
}

interface Fridge {
    void open();
    void close();
    void addSticker(Sticker s);
}

class Util {  
    void with(IOpenClose obj, Runnable r) {
        try {
            obj.open();
            r.run();
        } finally {
            obj.close();
        }
    }

    static void main(String[] args) {
        with(new File("temp.txt"), new Runnable() {
            public void run() {
                System.out.println("Hello, world!");
            }
        }
    }
}


OK, so the change wasn't really that big. But suppose Java did support operator overloading. Then look at this example:

interface VecArithmetic<E> {
    E operator + (E rhs);
    E operator * (E rhs);
}

class Vec3<E extends VecArithmetic<E>> {
    public E x;
    public E y;
    public E z;

    public Vec3(E x_, E y_, E z_) {
        this.x = x_;
        this.y = y_;
        this.z = z_;
    }

    public Vec3<E> operator + (Vec3<E> rhs) {
        return new Vec3<E>(x + rhs.x, y + rhs.y, z + rhs.z);
    }

    public Vec3<E> operator * (E rhs) {
        return new Vec3<E>(x * rhs, y * rhs, Z * rhs);
    }
}


This version should work for integers, floats, doubles or complex numbers (class Complex), or any type that defines right-addition and right-multiplication.

So, why would such implicit downcasting be preferable?
  • First, it doesn't require changing existing classes.
  • Second, classes such as Vec3 above, can effectively define a set of methods that they require from parameterized types. For example, some classes may only require operator +, where others require both operator + and operator *. The same effect could be achieved by constructing an interface hierarchy with interfaces IAdd and IMultiply, but this is superfluous and redundant.
  • Third, implicit downcasting is very easy to check statically. In the above example, the compiler doesn't have to work through the whole class Vec3 to see that it requires operator + and operator * from E. The legitimacy of the type parameter is only checked upon downcasting. This is vastly easier implement than C++ way latent typing, at the cost of some boilerplate code (e.g., interface VecArithmetic).
  • Ultimately, this method reduces what could be termed hierarchy interdependency: the class Vec3 and its parameterized type E no longer depend on common type hierarchy. This often obviates the need for adapter classes.
But are there any downsides? Well...
  • Explicitly marking a class C to adhere to some interface B increases documentability. If you know that "C is a B" and "A uses objects of type B", then you know objects of type C can be used by A. In the implicit conversion model, we only know that "A uses objects of type B": on the other hand, the classes conforming to interface B can be deduced automatically.

maanantai 26. toukokuuta 2008

Why C++ Still Kicks Ass

Take this simple class for three-dimensional vectors with parametrized types:

template <typename T>
clas
s vec3 {
public:
    T x;
    T y;
    T z;

    vec3(T const& x_, T const& y_, T const& z_)
        : x(x_), y(y_), z(z_) {}

    vec3<T> operator + (vec3<T> const& rhs) const {
        return vec3<T>(x + rhs.x, y + rhs.y, z + rhs.z);
    }
};

C# allows type parametrization, but unfortunately it cannot be combined with operator overloading: you cannot tell the compiler that "these 'T' things can be added too". Java doesn't allow type parametrization over base types, or operator overloading for that matter, so it's off even worse.

C++ makes such code possible because it uses latent typing: the type of T is tracked throughout compilation, and if T doesn't support addition, a compile-time error will be issued. However, if things go wrong, this can lead to pretty astonishing compilation errors.

In the next post, I will present a proposal for adding such latent typing for C# and Java, but considerably more easily than in C++.

keskiviikko 16. huhtikuuta 2008

Verbosity of Non-closured Java Violates the Law of Demeter

It is well known that Java programs (at least of the non-closured kind) tend to be rather verbose. Some people actually even prefer this, since it may make program more readable. In this post, however, I will try to show that verbosity isn't just a local issue. Rather, it affects code quality on a larger scale by increasing temptation to bad design - specifically, violating the law of demeter. Take the following simple Person class:

class Person {
    String name;
    int age;
}


Now assume we have a method containsName:

boolean containsName(List<Person> list, String name) {
    for(Person p : list) {
        if(name.equals(p.name)) {
            return true;
        }
    }
    return false;
}

Notice that the method is only interested in the names of the persons, so its dependency on class Person is merely accidental, unless we anticipate containsName to later utilize other details of class Person. In other words, methods like containsName tend to violate the law of demeter since they need to access the (sub(sub(sub)))fields of their arguments. This results in dreaded "train code", e.g., getInstance().getCompany().getPersons().first().getName().

Here's the version without the dependency:

boolean contains(List<String> xs, String y) {
    for(String x : xs) {
        if(x.equals(y)) {
            return true;
        }
    }
    return false;
}

Such contains method is actually already included in List, so the above method is redundant. However, calling this method is a nightmare since we must "extract" the names of the Persons manually:

List<String> names = new ArrayList();
for(Person p : persons) {
    names.add(p.name);
}
contains(names, "Mikko");

To summarize: the hardness of extracting container fields results in temptation to use less verbose code that violates the law of demeter.

Luckily, with closures, the above will become clearer:

contains(persons.map({Person p => p.name}), "Mikko")

or, equivalently

persons.map({Person p => p.name}).contains("Mikko")

To preserve performance, introducing another method taking a key closure
is warranted:

persons.find({Person p => p.name}, "Mikko")

torstai 20. maaliskuuta 2008

Obtaining BGGA Closure Parameter Types at Runtime

Recently Alex Miller has explored the use of BGGA closures for building dynamic visitors. Such visitor builders, and also Ricky Clarkson's closure-based pattern matcher, could be greatly simplified if we could obtain the parameter types of the closures at runtime. More precisely, given a closure {T => void}, where T is a parameterized type, we are interested in knowing the class of T.

Since Java generics are implemented via erasure, the generic parameter types can easily be lost. Some ways of "manually" reifying the types include:

  • Passing around class literals, e.g., String.class and Integer.class.

  • Subclassing a generic class with specified parameter types. The parameter types can then be read off using getGenericSuperclass method of the associated class object. This is the famed Gafter's gadget.
However, in the case of BGGA closures, the information about parameter types is already present and needs no manual reification. There are at least two ways to obtain it:

  • Using reflection to read the type parameters of the invoke method of the closure. In the current (2008-02-26) BGGA prototype, the invoke method is always the first method in the array returned by getMethods of the closure's class object, which simplifies the implementation.

  • Using the getGenericInterfaces of the closure's class object. BGGA closures implement generic interfaces from package javax.lang.function, and the type parameters can be read out similarly as with Gafter's gadget.
Below is an example of the first method. The second method is probably too brittle given its high reliance on the current implementation of the BGGA prototype.

import java.util.Map;
import java.util.HashMap;

class Visitor<T> {
    private final Map<Class,Object> cases = new HashMap<Class,Object>();

    static <T> Visitor<T> make() {
        return new Visitor<T>();
    }

    <U extends T> Visitor<T> add({U => void} block) {
        Class argClass = block
            .getClass()
            .getMethods()[0] // invoke
            .getParameterTypes()[0];

        cases.put(argClass, block);

        return this;
    }

    @SuppressWarnings("unchecked")
    void visit(T arg) {
        Class argClass = arg.getClass();

        {Object => void} block = ({Object => void}) cases.get(argClass);

        if(block != null) {
            block.invoke(arg);
        }
    }

    public static void main(String[] args) {
        Visitor<Object> v = Visitor.make()
            .add({String s => 
                System.out.println("Mm, I love strings!");})
            .add({Integer i => 
                System.out.println("An Integer is fine too");});

        v.visit("Mikko");
        v.visit(24);
    }
}