When I find something interesting and new, I post it here - that's mostly programming, of course, not everything.

Tuesday, June 23, 2009

Why Monoids?

I sincerely plan to explain in simple terms what's all this fuss about monoids, why we suddenly need these primitive structures, and how to make them available in Java.

A monoid is one of the simplest notions of algebra. Slightly oversimplifying, it consists of elements that have a multiplication operation a*b defined on them, and a neutral element e, such that e*a == a*e. Multiplication should be associative (a*b)*c == a*(b*c), but not necessarily commutative (a*b != b*a). If you can't imagine non-commutative multiplication, think of matrices... or of spatial rotations. The order counts.

A simpler example would consist of integer numbers and addition playing the role of multiplication. 0 is the neutral element with respect to addition, so there.

Now why would we need this structure? It turned out that if you have to add up a billion, or a quadrillion of integers, you may be interested in doing it by distributing the operation over several (or ~700 000, in the case of Google) machines. Adding up a quadrillion of integers may be not that interesting; but multiplying a billion of matrices is a different, and more challenging, story.

And why can we distribute it among the thousands of computers? We can do it all thanks to the associativity. (m1*m2*...*mk1*mk1+1*mk1+2..*mk2*...*mn) is the same as
(m1*m2*...*mk1)*(mk1+1*mk1+2..*mk2)*(...*mn), and we can calculate intermediate results (shown in parentheses here) on different machines, then aggregating them together; we can do it in cascade, whatever we choose. The strategy does not influence the result.



The following Java class defines, or rather declares the necessary functionality:
public abstract class Monoid<X>
{
abstract X unit();
abstract X m(X x, X y);

public X fold(Iterable<X> source) {
X result = unit();
for (X x : source) {
result = m(result, x);
}
return result;
}

public X fold(X[] source) {
X result = unit();
for (X x : source) {
result = m(result, x);
}
return result;
}
}
We can use it to add numbers, strings, matrices; to multiply numbers or matrices; to concatenate lists and join sets.
Monoid<Set<T>> setMonoid = new Monoid<Set<T>> {
public Set<T> unit() {
return Set<T>.EMPTY;
}

public Set<T> m(Set<T> a, Set<T> b) {
Set<T> c = a.clone();
c.addAll(b);
return c;
}
Of course using all this for the data that we have in memory of one single machine. But, as I said before, we can actually delegate the monoid's fold operation to the computing cloud, and have the list deployed somewhere in the big file system, and let the cloud partition it. The result will be the same; and all this due to the monoidal properties of our concrete classes and operations.

Sunday, June 14, 2009

on Variance

I'll try to explain to practicing programmers what this variance means, and how it is related to the famous and frequently misunderstood "Liskov principle".

Say we have the following types: A, B, A1 and B1, where A1 is a subtype of A, and B1 is a subtype of B. In Java it would look like this:

class A {
...
}

class A1 extends A {
...
}

class B {
...
}

class B1 extends B {
...
}


Now let's build a class of pairs:

class Pair<X, Y> {
X first;
Y second;
}


If we compare Pair<A, B> ab; and Pair<A1, B1> a1b1; we see that it is natural to expect that a1b1 should be assignable to ab, but not vice versa: the components can be cast in one direction only. A1 is a specialization of A; B1 is specialization of B, and so the Pair<A1,B1> is a specialization of Pair<A,B>. This is what is called covariance: specialization transforms into specialization.

And if you are a fan of "Liskov's substitution principle", you can see that a Pair<A1, B1> can always be used ("substitute") where a Pair<A, B> is required.

Now the opposite case, contravariance. Suppose we have two functions declared like this:

B f(A a);
B f1(A1 a1);

(I use Java syntax; the two lines above mean that both functions return B).

Which one can replace which? See, f1 cannot be used instead of f: what if we pass something that is not A1? f1 does not know what to do with it. But the opposite is okay: we can always use f where f1 is required: any A1 can be viewed as an A.

When we deal with object methods, not just plain functions, one hidden parameter is always present: the object itself. So that a method declared as

class A {
...
C f(B b);
...
}

actually is a function defined on pairs (A, B), and takes values in C. This function is contravariant in both parameters. Contravariance in the first (hidden) parameter is an essential part of OOP. Take a look at the following code snippet:


class A1 extends A {
...
C f(B b) {
println(getClass().getName() + " called with " + b.getClass().getName());
}
}
...
A instanceOfA = new A1();
...
instanceOfA.f(b);
...
}


This is Java, and Java uses dynamic dispatch (that is, all methods are virtual), and so the method of A1 is called in place of method f of A. Method f of A is substituted with a method of A1; contravariance made it possible.

Note that, since a method is always contravariant on its owner object, all getters, for instance, are contravariant: a superclass can call them, and the owner object, which is an instance of a subclass, can substitute the superclass in any context.

(Many thanks to M.Abadi and L.Cardelli's "A Theory of Objects" for explaining the OOP part to me.)

Saturday, April 04, 2009

inheritance, delegation, decoration, gravitation

I think I have some ideas regarding what is the problem with inheritance and, specifically, multiple inheritance. Let's start with geometry.

A plain pedestrian perceives 2-dimensional space as some kind of lawn, where you can walk back and forth, and know your position by measuring the distance towards the fence. (That's like the Bible's universe, except that the Bible universe is 3d, 100 miles by 100 miles by 5 miles.) So people think, aha, I can describe the ball as a point in space, 10 feet from the back fence ('x'), 30 feet from the left fence ('y').

Then 3d comes into the picture. We programmers don't deal with 3d. We deal with element's position in the window. But there's 3d out there. And the third dimension is spectacularly different. If you place a point somewhere, it will fall to the surface. The height from which it falls is called 'z'. It is like the other two coordinates, but it's different, because it falls if you don't hold it. So the point in space is the point on the lawn decorated by the height at which it is currently held. Be real, the force of gravitation will drag it to the surface anyway, it is not natural for an average point to have a height.

In your code you write it like this:

class Point {
double x;
double y;
}

class ThreeDPoint extends Point {
double z;
}


The software design purists tell us that we should write

class ThreeDPoint {
Point point;
double z;
}


This way we won't be mixing the two kinds of points and, say, checking whether an instance of ThreeDPoint lies between two Points... even equality checking may be problematic, where the Point(1, 2).equals(ThreeDPoint(1, 2, 3)) but not vice versa. Josh Bloch told us in his books that equality should be symmetrical.

I don't know if you read in some other book that it does not make any sense to define equality for objects of different types. Let me try to show why. You have one function that takes user name and returns user age (doing some magic, like guessing that a Sue should be over 70 and a Ryan should be under 35); the other function takes an integer number and returns its decimal representation. Can you seriously discuss the idea of checking whether these two function can have a common value for a certain argument? (In a sense they actually can: a one-year-old Japanese boy named Ichi, for instance).

But let's return to the three-dimensional space. Now that we have successfully (double-successfully, since we have two solutions) defined a three-dimensional point, let's thing about rotating the coordinates. Not that we do it here on our flat Earth, but on the space station they do it all the time. Have you heard, they have no gravitation! There's no special 'z'; all three dimensions are made equal. And the idea that a three-dimensional point is some kind of extension of the idea of a two-dimensional point does not fly in space. What do you think is the location of, say, Mars? What is the height of Mars? Mars does not have height. Even our Earth does not have height.

To switch from one coordinate system to another we need to do what? Shift and rotate. Now it turns out that the three dimensions are made equal; the space is (actually it is not, but that's the latest news from astrophysics) homogeneous and isotropic (or else we would be able to save the energy, according to a certain theorem).

Our representation of a 3-d point as a decoration of a 2-d point just does not work.

For a mathematician I'll express it like this: R3 is isomorphic to Rx(R×R) but not equal. See the difference? For a programmer, it looks like this:

(x, y, z) can be modeled as (x, (y, z)), or as ((x, y), z), or as (y, (z, x)), or as ((z, 42), (y, 55, x)) - but they are all different things. Only gravitation was forcing us, earthlings, to think of (x, y, z) as of ((x, y), z).

There are relations between 2d and 3d, though. A 3d point can be projected to 2d. (x, y), and (x, z), and (y, z) are all projections to the appropriate planes.

But what I wanted to say is this. If you are thinking of decorating your class with some more attributes, think again. Probably what you actually need is another class with natural projections (they are called views in SQL) to your old class. We tend to think that some attributes are more "external" than the "internal" attributes. Say, an object id is in the heard of your design, right? Are objects "decorations" of their ids? We don't think so, do we?

Oh, and about the "diamond inheritance problem". Do you know how relational databases solve this problem? Check it out. It's called "normalization".

Sunday, March 15, 2009

Cursor vs Flyweight

It is a pretty frequent problem: you need a million instances (ok, a billion if you are on a larger machine), and all the data are available, but you are not sure about overloading your Garbage Collector (I'm talking about Java, but in C you also have to deal with pesky memory issues... I guess). Will the GC handle a million (or a billion) of records that just come and go?

A known Flyweight Design Pattern, in its Java incarnation, suggests to do this: create an instance and cache it in a WeakReferenceMap, as if the weakness of the reference saves any memory at any given moment. No; we still have to create all those instances:

for (Record r : allMyRecords) {
// do something
// forget about record r
}


A typical implementation would be like this:

RecordSet allMyRecords;


where

class RecordSet implements Iterable<Record> {
...
Iterator<Record> iterator() {
...
Record next() {
// get the record from somewhere and return it:
Record theRecord = new Record(...);
return theRecord;
}
...
}
...
}


Say, we assume that all the data are actually in memory, but have a different form, so that for a record class like

interface Record {
long id();
String firstName();
String lastName();
String phone();
String email();
}


we actually have some kind of storage that keeps the data:

Map<Long, String> names;
Map<Long, String> phones;
Map<Long, String> emails;


If we implement the class Record so that it stores first/last name, phone and email, we duplicate a lot of data; so maybe it would be enough to store just something, just an id, and retrieve the rest on demand. For this, we will have a more-or-less lightweight implementation:

class LightRecord implements Record {
long id;
String phone() { return phones.get(id);}
String email() { return emails.get(id);}
String firstName() { return names.get(id).split(" ")[0];} // not very smart
String firstName() { return names.get(id).split(" ")[1];} // not very smart either
}


So we will store at least million (a billion) longs, and probably more. Wasting our memory, even if GC takes care of it. On a cellphone you probably should not count on gc.

So what can we do? Here's a solution that does not take any extra memory. It's like a cursor. It points to an imaginary record. Of course this record changes all the time, so if you want to really save an instance, do it via copy constructor. Otherwise, use it and forget it:


class RecordSet implements Iterable<Record> {
long currentId;
Record currentRecord = new Record() {
String phone() { return phones.get(currentId);}
String email() { return emails.get(currentId);}
String firstName() { return names.get(currentId).split(" ")[0];}
String firstName() { return names.get(currentId).split(" ")[1];}
}
...
Iterator<Record> iterator() {
...
Record next() {
currentId = ...;
return currentRecord;
}
...
}
...
}


So, the only time a constructor is called is when the iterator is created. Neat, eh?

The solution is not new. It is probably 50 years old. It is just properly rephrased in Java.

Wednesday, January 07, 2009

Incrementally calculating factorsets (Java class)

Recently I encountered a problem of calculating a factorset, given a set and a binary relationship. If you ask wtf is binary relationship, it is a predicate defined on pairs of elements:
interface BinaryRelationship<X, Y> extends Predicate<Pair<X, Y>> {}

Well, of course Java does not have type variables, so that one cannot interchange these two definitions... poor, poor Java programmers, poor, poor me.

But anyway.

Oh, and if you ask wtf a factorset is, let me introduce.

Definition
A binary relationship R on set A is called reflexive if aRa for all a∈A.

Definition
A binary relationship R on set A is called symmetric if aRb => bRa for all a∈A, b∈A.

Definition
A binary relationship R on set A is called transitive if aRb && bRc => aRc for all a∈A, b∈A, c∈A.

Definition
Given a set A and a reflexive, symmetric, transitive binary relationship R on A, an equivalence class over R is any such subset A1 of A that first, if x∈A1 and y∈A1 then xRy, and second, if x∈A1 and xRy then y∈A1

Definition
Given a set A and a reflexive, symmetric, transitive binary relationship R on A, a factorset A/R is defined as a set of all equivalence classes over R.

Example
Take integer numbers, and let nRm if n - m is divisible by 2. We'll have just two equivalence classes, odd numbers and even numbers. By the way, guess why two equivalence classes never intersect.

Anyway.

One can calculate a factorset even if the relationship is not symmetric, reflexive or transitive. Just produce a symmetric, reflexive transitive closure, and factor over it. The problem is, calculating a transitive closure may be costly. So, for practical reasons, I have implemented factorsets in a non-lazy way. And then I realized that, on some occasions, to provide the necessary relationship, I had to build a set of pairs, and then provide the relationship that checks if a pair is in that set, and then calculate the factorset that transitively closes the relationship. Pretty stupid, why not just do it incrementally?

That's how I got this class:

public static class FactorSet<X> {
Set<X> set;
Map<X, Set<X>> equivalenceClasses;

/**
* Builds an initial factorset, given the domain set.
*
* @param set domain set.
*/
public FactorSet(Set<X> set) {
this.set = set;
equivalenceClasses = new HashMap<X, Set<X>>();
for (X x : set) {
equivalenceClasses.put(x, Set(x));
}
}

/**
* Builds a factorset of a given set, by the transitive closure of a given relationship.
*
* @param set base set
* @param r binary relationship
*/
public FactorSet(Set<X> set,  BinaryRelationship<X, X> r) {
this(set);
factorByRelationship(r);
}

/**
* Given a <code>BinaryRelationship r</code>, merges equivalent classes if they
* contain elements that are in <code>r</code>.
*
* @param r the binary relationship. Does not have to be symmetrical or transitive.
*/
public void factorByRelationship(BinaryRelationship<X, X> r) {
for (X x1 : set) {
for (X x2 : set) {
if (x1 == x2) {
continue; // skip same value
}
if (equivalenceClasses.get(x1) == equivalenceClasses.get(x2)) {
continue; // skip same class
}
if (r.eval(x1, x2) || r.eval(x2, x1)) {
merge(x1, x2);
}
}
}
}

/**
* Merges equivalence classes for two elements
* @param x1 first element
* @param x2 second element
*/
public void merge(X x1, X x2) {
Set<X> class1 = equivalenceClasses.get(x1);
Set<X> class2 = equivalenceClasses.get(x2);
Set<X> merged = new HashSet<X>(class1);
merged.addAll(class2);
for (X x3 : merged) {
equivalenceClasses.put(x3, merged);
}
}

/**
* @return the latest version of factorset built here.
*/
public Set<Set<X>> factorset() {
return new HashSet<Set<X>>(equivalenceClasses.values());
}

/**
* @return the function from the domain set to the factorset.
*/
public Function<X, Set<X>> asFunction() {
return Functions.forMap(equivalenceClasses);
}

/**
* @return the domain set.
*/
public Set<X> domain() {
return set;
}
}

Neat, right? Look at this unittest:
public void testFactorSet_incremental() {
Set<Integer> set = Set(1, 2, 3, 4, 5);
FactorSet<Integer> factorset = new FactorSet<Integer>(set);
factorset.merge(1, 3);
factorset.merge(3, 5);
factorset.merge(4, 2);
Set<Set<Integer>> expected = Set(Set(1, 3, 5), Set(2, 4));
assertEquals(expected, factorset.factorset());
}

(Don't get confused by the functional-programming style of set constructors. Just open The Scala Book, they do it everywhere!

If you find a better way to do this thing, please let me know.

Thursday, December 25, 2008

Inner Class in Java: Unexpected Volatility

You probably know that if you use a value in an inner class, the evil Java compiler wants you to write "final"; people do all kinds of trick to bypass it - store date in arrays, for instance.

But relax, you do not have to write "final" if it is a member field. Look at this code:

package experimental;

import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class VolatileConstant {

public static void main(String[] args) {
Iterable<Set<String>> iss = new Iterable<Set<String>>() {

public Iterator<Set<String>> iterator() {
return new Iterator<Set<String>>() {
int i = 0;
public boolean hasNext() {
return i < 3;
}

public Set<String> next() {
i++;

return new AbstractSet<String>() {
public Iterator<String> iterator() {
return new Iterator<String>() {
int j = 0;
public boolean hasNext() {
return j < i;
}

public String next() {
j++;
return "" + i + "." + j;
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}

public int size() {
return i;
}
};
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}
};

Set<Set<String>> sss = new HashSet<Set<String>>();

for (Set<String> ss : iss) {
sss.add(ss);
System.out.println(ss);
}
System.out.println(sss);
}
}


You know what it prints? It's in the next line, in white color so that you have a choice not to see it.

[1.1]
[2.1, 2.2]
[3.1, 3.2, 3.3]
[[3.1, 3.2, 3.3], [3.1, 3.2, 3.3], [3.1, 3.2, 3.3]]


I am sure you can explain it, after a while, but is not it really amusing?

Saturday, December 13, 2008

Functions

An Excuse

Well, I decided to gather all the code related to Function class in one "universe" container class, Functions. The other option would be to have a separate "package". I do not see any reason for using packages instead of container classes.

My idea regarding container classes is that it is a theory - we have constants, types, rules, axioms, all in one place. An element of theory is meaningless outside of its theory. A set does not make sense without a Set Theory.

So here we have some kind of "Functions Theory". Since functions are defined on types, which vaguely are like classes, which vaguely are like unlimited sets, we can define set-theory-like notions of Injection and Bijection. A Surjection would be nice to have too, but it has no functional influence on current programming practice: nobody knowingly builds factorsets.

Overview

Three abstract classes are defined: 
  • Function - general functionality;
  • Injection - a special kind of Function, not mapping different values to one;
  • Bijection - invertible Function
and several useful extra classes: 
  • Inclusion, - a Function that includes one set into another; 
  • Id - an identity Function
  • PairsToMap - takes a set of pairs, turns it into a Map;
  • IterableToSet - takes an iterable, turns it into a Set.
Also there are several  static methods. Don't be afraid of static methods, they all belong to Functions Theory, and there's hardly a reason to override or polymorph them in any way.

Function

A Function<X, Y> declares an abstract method Y apply(X x), which obviously determines the function's behavior. In addition, Function has methods for mapping an Iterator, an Iterable, a Collection, a List. Note that there's no method that maps a Set. Why? Because if you apply a Function to a Collection, you can have repeating values.

Method toMap(Set<X) converts a Function into a Map; method forMap(Map<X, Y>) turns a Map into a Function. Static compose(f, g) method composes two Functions.

Injection

Injection is a special kind of Function: it maps different arguments to different values. Not that we can verify it; we just trust the declaration. Since a composition of two Injections is an Injection, this case is taken care of. It is. 

An Injection maps a Set to a Set, so this method is there.

Inclusion is a special kind of Injection: it includes one class (set) into its superclass (superset). It maps a value to itself, casting.

Bijection

Bijection<X, Y> is a special kind of Injection: it has an inverse. So, additionally, it declares a method X unapply(Y). A composition of two Bijections is a Bijection


Here is a sample code, where Schwartzian transform is defined:

/**
* Given a function f, builds another function that for each x
* builds Map.Entry(x, f(x))
*
* @param f function to apply
* @return a function that builds pairs.
*/
public static <X, Y> Bijection<X, Pair<X, Y>> schwartzianTransform(final Function<X, Y> f) {
return new Bijection<X, Pair<X, Y>>() {
public Pair<X, Y> apply(final X x) {
return math.cat.LazyPair.LazyPair(x, f);
}

public X unapply(Pair<X, Y> p) {
return p.x();
}
};
}


Oh, and the code is here.

Thursday, December 11, 2008

Java Solutions: inheriting chain calls

Recently chain builder calls in Java have been gaining popularity, look, e.g. at this.

The idea is that you have a builder that has small configuration methods and in the end builds the right stuff. StringBuffer works like this:

 return new StringBuffer().append("Now is ").append(new Date()).append(", and ticking...").toString();


Or, more typically, something like this code:

new TestBuilder().limitTime(1000).forPackage(this.getClass().getPackage()).skipFlaky().build().run();


This way we, first, annotate our parameters, and second, bypass the boring law that demands writing each statement on a separate line.
And of course it makes the process more flexible.

Actually, the fact that it is a builder is unimportant. We can just chain calls if every method that in the previous life was returning void would start returning this.

But there's a problem here... what if our builder is not the ultimate version, but a subclass of a superbuilder? Look at an example below:


public class ChainCalls {
public static class A {
A do1(String s) {
System.out.println("A.1 " + s);
return this;
}

A do2(String s) {
System.out.println("A.2 " + s);
return this;
}
}

public static class B extends A {
B do0(String s) {
System.out.println("B.0 " + s);
return this;
}

B do1(String s) {
System.out.println("B.1 " + s);
return this;
}

B do3(String s) {
System.out.println("B.3 " + s);
return this;
}
}

public static void main(String[] args) {
((B)(new B().do0("zero").do1("one").do2("two"))).do3("three");
}
}


See how we have to cast the result of do2()? That's because the method of class A has no knowledge that it actually works in the context of class B.

How can we deal with this? One cheap solution is to duplicate all the methods in B and call the delegate, that is, super.do1() etc. Feasible, but not very nice, not very scalable, and not very Java5-ish.

Because we have generics, and we have a so-called covariant inheritance, so that - because we can!

An anonymous contributor in livejournal.com has suggested the following solution:

public class ChainCallsCovariant {
public static abstract class SuperA<T> {
abstract T self();

T do1(String s) {
System.out.println("A.1 " + s);
return self();
}

T do2(String s) {
System.out.println("A.2 " + s);
return self();
}
}

public static class A extends SuperA<A> {
A self() { return this; }
}

public static abstract class SuperB<T> extends SuperA<T> {
T do0(String s) {
System.out.println("B.0 " + s);
return self();
}

T do1(String s) {
System.out.println("B.1 " + s);
return self();
}

T do3(String s) {
System.out.println("B.3 " + s);
return self();
}
}

public static class B extends SuperB<B> {
B self() { return this; }
}

public static void main(String[] args) {
new B().do0("zero").do1("one").do2("two").do3("three");
}
}


The main trick here is to encapsulate "return this", and make it generic, so that we always, in the eyes of the compiler, return the instance of the right class.

P.S. Here I've posted a better solution.

Thursday, December 04, 2008

Java Compiler Warnings 2

Say, somewhere in your test code, you need a list of nulls:

List<Direction> directionsWithNullValues = Lists.newArrayList(null, null, null);


Ouch! Compiler warning: unchecked generic array creation of type E[] for varargs parameter

What's going on here? An array can be created, but Java is confused regarding the type. Just cannot deduce it.

The solution is simple (although awkward):

List<Direction> directionsWithNullValues = Lists.newArrayList((Direction)null, null, null);

Java Compiler Warnings 1

Since out of the 5 million Java programmers there's hardly 100 that actually know Java (I do not count myself in that holy hundred), we probably have to join our efforts (like people do at javaranch and help each other.

Here I'm going to post typical Java Compiler warnings that I encounter and fix.

So this chain letter is something like "effective java for dummies", so to say.

1. Creating generic arrays.

Out of your best intentions, you decide that instead of manually concatenating your collections, you'll have something like this:

Collection<T> concat(Collection<T>... collections) {
Collection<T> result = new ArrayList<T>();
for (Collection<T> collection : collections) {
result.addAll(collection):
}
return result;
}


Let's forget the dubious decision of making a live arraylist out of collections of unknown nature, instead of just having a lazy collection or iterable. What's the problem here? Will our compiler complain? No. But if we try to use it, like this:

Collection<Integer> newCollection(oldCollection1, oldCollection2);


we will get a compiler warning: "generic array creation" warning. What's wrong? Here's what. When you pass around a vararg list of parameters, implicitly an array is being created. But Java does not like creating arrays of elements which type is generic. Hence the warning.

To avoid, we can just write a simpler version:

Collection<T> concat(Collection<T> collection1, Collection<T> collection2) {
Collection<T> result = new ArrayList<T>(collection1);
result.addAll(collection2):
return result;
}
...
Collection<Integer> newCollection(oldCollection1, oldCollection2);


Not that I neither condone nor discuss the idea of building an arraylist - this deplorable issue is just outside of the narrow boundaries of this topic.

Sunday, November 23, 2008

Keyboard Bookmarklets

Find your language below, and drag the grey square to bookmarks tab in your browser.

Next time you open a page with edit field (e.g. blogger), click the bookmark in the bookmarks tab,
and here you go, the keyboard pops up.

So far it works only on Firefox, Google Chrome and Safari, though.





sq Albanian  hu Hungarian  ro Romanian  
ar Arabic  il Israel(Ar,En,He,Ru)  ru Russian  
hy Armenian  is Icelandic  sr Serbian  
az Azeri  kn Kannada  si Sinhalese  
be Belarussian  kk Kazakh  sk Slovak  
bn Bengali  km Khmer  special Special (Arrows etc)  
bg Bulgarian  lv Latvian  tg Tajik  
hr Croatian  lt Lithuanian  ta Tamil  
cs Czech  mk Macedonian  tt Tatar  
et Estonian  ml Malayalam  te Telugu  
fa Farsi  mr Marathi  th Thai  
fi Finnish  mn Mongolian  tr Turkish  
ka Georgian  no Norwegian  tk Turkmen  
gu Gujarati  ps Pashto  ug Uighur  
hi Hindi  pl Polish  ua Ukraine (Ua,Ru)  


Questions? Write me.

Sunday, November 16, 2008

What To Do with Dispatch of Static Methods for Java

A problem with Java that everybody encounters from time to time is the following: static methods cannot override each other. If class A has static method m, and class B has static method m, with the same signature, there is no automatic way to figure out which method should be called.

Look at these two classes:

public class A {
String x;

public A(String x) {
this.x = x;
}
public String toString() {
return "A(" + x + ")";
}

public static A combine(A a1, A a2) {
return new A(a1.x + " + " + a2.x);
}
}


and

public class B extends A {

public B(String x) {
super(x);
}
public String toString() {
return "B(" + x + ")";
}

public static B combine(B b1, B b2) {
return new B(b1.x + " * " + b2.x);
}
}


I would be nice if the right combine method would be called in each case. But no, the following code
import static A.*;
import static B.*;

public class Test {
public static void main(String[] args) {
A a1 = new A("a1");
A a2 = new A("a2");
B b1 = new B("b1");
B b2 = new B("b2");
A c = new B("c");
System.out.println(combine(a1, a2));
System.out.println(combine(b1, b2));
System.out.println(combine(a1, b2));
System.out.println(combine(b1, c));
}
}

will print
A(a1 + a2)
B(b1 * b2)
A(a1 + b2)


A(b1 + c)


Why does the second call prints B(b1 * b2)? Just because the method combine that takes two arguments of type B is available via static import B.*.

Interesting, we know the objects types, but dispatch is done statically. Can we fix it?

One awkward solution is to use reflection: add the following to A
public static A smartCombine(A a1, A a2) {
Class class1 = a1.getClass();
try {
Method method = class1.getMethod("combine", class1, a2.getClass());
System.out.println(method);
return (A)method.invoke(null, a1, a2);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

then call it like this:
System.out.println(smartCombine(b1, c));

and we'll get
B(b1 * c)

Okay, not the best solution anyway. We won't seriously add a "smart" version to each static method.

So maybe we should not use static? Should we hate static? Let's define instance methods, in class A:
public A combineWith(A other) {
return new A(this.x + " | " + other.x);
}

and in class B:
public A combineWith(A other) {
return new A(this.x + " & " + other.x);
}

public B combineWith(B other) {
return new B(this.x + " && " + other.x);
}

Now let's try this:
System.out.println(b1.combineWith(b2));
System.out.println(b1.combineWith(c));
System.out.println(c.combineWith(b1));

We well see the following:

B(b1 && b2)
A(b1 & c)
A(c & b1)

What went wrong? The first case is ok, but how about others? The second case is due to the simple fact that the dispatch is done statically. So the compiler sees the parameter is an A, and calls the method that takes an A.
How about the next one, we use two instances of B, but still? It is trickier. The compiler sees an instance of A, and calls a method combineWith of class A; this method takes an A as a parameter. At runtime, since c is an instance of B an appropriate method of B is called; but the signature still is combineWith(A), and this sad truth destroys our plans.

So, what can we do? double dispatch should help, right? Here is a good example of how to use double dispatch in in Java.

Unless you do not know already, the trick is this. We have class A and and its subclass B, and a parallel hierarchy, X and its subclass Y. These two hirerachies interoperate, and we want to make sure that if, say, an instance of B works with an instance of Y, it would recognize this even if the instances are passed around as instances of A and X.

For double dispatch to work, class A should be aware of classes X and Y; similarly, class X should be aware of A and B.

Not so in our case; we have just one hierarchy, and of course our A is not aware of its subclass B.

So our naive attempt of using double dispatch will fail.

First, add to A the following:
static A combine(A a1, A a2) {
return a1.combineFollowedBy(a2);
}

A combineFollowedBy(A other) {
return other.combinePrecededBy(this);
}

A combinePrecededBy(A other) {
return new B(this.x + " | " + other.x);
}

and add to B a similar code:
A combineFollowedBy(A other) {
return other.combinePrecededBy(this);
}

A combineFollowedBy(B other) {
return other.combinePrecededBy(this);
}

A combinePrecededBy(B other) {
return new A(this.x + " && " + other.x);
}

A combinePrecededBy(A other) {
return new A(this.x + " & " + other.x);
}

As I already said, this won't bring happiness in this world:

A(a2 | a1)
A(b2 & b1)
A(b2 & a1)
A(c & b1)


The reason is obvious: nowhere in our call sequence for, say, c and b1 we have a chance to call B.combineX(B)

So, what should we do? For a case like this I have only one suggestion: use instanceof.

We don't need double dispatch. Let's simplify A:
static A combine(A a1, A a2) {
return a1.combineFollowedBy(a2);
}

A combineFollowedBy(A other) {
return new A(this.x + " | " + other.x);
}


And make B a little bit less kosher:
A combineFollowedBy(A other) {
return other instanceof B ?
combineFollowedBy((B)other) :
super.combineFollowedBy(other);
}

A combineFollowedBy(B other) {
return new B(this.x + " & " + other.x);
}


Now it works. A small stylistic sacrifice for fixing the lack of functionality.

Followers

Subscribe To My Podcast

whos.amung.us