Thursday, May 31, 2007

Log4j code template in Eclipse

I just tried adding my own code template to Eclipse, and wanted to recommend it to Java programmers, if you can identify a pattern you use all over the place. For me, it was the way I use log4j. Just about every class ends up declaring a Logger field, which all look like this:

private Logger m_logger = Logger.getLogger(getClass());
Even with Eclipse's tab completion accelerating this, it started to get tedious. Now, I just type logger, press Ctrl-Space and enter to confirm the selected template is what I meant. I do then have to use Ctrl-1 to Quick Fix the error that I need to import the log4j package. That's the only potential improvement I've identified.

Actually, it gets better. Instead of using the method call getClass(), I've seen some people use the static class member class, as in MyCollection.class. There's presumably some infinitesimal speed difference, I don't know as I've never looked into it. Frankly, before I thought of these templates I used the getClass() approach because it was faster for me to type in Eclipse. Once I've typed Logger.getLogger(g, I hit Ctrl-Space for the auto completion list and hit return since getClass() is the top choice. Now with the custom templates, I can get the Classname.class form for free by using a template variable:
private Logger m_logger = getLogger(${enclosing_type}.class);
If everything stays in Eclipse, even the hard-coded class name is no problem because it gets renamed when the class or file gets renamed.

To get started writing your own template, open Window > Preferences... and select Java, Editor, Templates.

Some thoughts on code templates

Java is an OOS, Object Of Scorn, for many functional and dynamic scripting programmers, partly for its verbosity. Just as VC++ helped me deal with C++, Eclipse is helping me deal with Java by providing some decent accelerators like these templates. The fact that I can write my own is nice. It's my opinion that even in the most elegant languages I know of, it's not always possible to factor out every pattern in a large code base to an tiny pearl of beauty. I think you inevitably end up with a few code patterns of significant size that could benefit from editor templating.

Tuesday, May 15, 2007

Franchised web apps

It's time for another free-for-the-implementing business idea. This one: web site franchises.

Take the software that runs flickr or reddit or any of a thousand other things, and package it so it can be installed on a server farm elsewhere and customized. The example of reddit is perhaps most compelling to me as regards customization, because it's easy to see that the format of the software is so independent of its content, yet the social way reddit functions could be adapted to other groups of people. Right now it would be hard to grow a home improvement user base on reddit because of the slant of the existing user base. But somebody could buy a home improvement reddit franchise and start it up. This would be a way for the creators of a web application to get into markets they know little about.

I can think of two possible drawbacks for franchising a given web application. The first drawback is the financial aspect. If the application is having problems turning a profit with the existing international user base, fragmenting it would not be an option. There would have to be the potential for customizing the application in a way that adds value. The second potential drawback is the task of packaging the web application for replication. In most cases, this would be a non-trivial development cost. Designing 3D buttons and Javascript animations is fun and can be taken to show-and-tell, writing install scripts can be a tedious and under-appreciated chore.

Another angle to the web app franchise idea is some things may be salable to large corporations and government agencies for internal use without concerns about compromising confidential information over public networks.

As with most (all) of my ideas posted here, I expect this one is so good it was done long ago and I just now finally forgot enough of its origins that I think it's new. Please correct me. If I happen to be wrong, and you make some real money with this idea, I'd appreciate a link back to this blog, with due credit: "Thanks to JFKBits, whoever or whatever that is" would be fine.

Thursday, May 10, 2007

Feed the burner

I just registered JFKBits with FeedBurner. If you please, take a moment to re-subscribe so I can get an accurate count of regular readers.

Tuesday, May 08, 2007

Iteration over neighbors

In working on a graph problem, I came up with a distant cousin of fold, which I'm calling foldNeighbors. I entertain no delusions this is anything new, but it was fun and wanted to share its derivation. Maybe someone knows a name for it.

Start with a list of vertices, taken to be a path through a graph, with the intent of summing the weights of the edges. The accompanying picture shows a graph of major US airports and some typical non-stop airfares that the Traveling Language Enthusiast might pay this week.

vertices = [ORD, SJC, AUS, JFK, TPA, LAX]


Note I'm using Standard ML-like notation here, []'s delimit lists, :: is cons or list prepend, and currying is rampant.

Decompose into edges and fold

A simple way to understand the problem is to split the vertices list into a list of edges, i.e. pairs, map the weight function W onto it, and feed that to fold for summing:
Given E = [[ORD,SJC], [SJC,AUS], [AUS,JFK], [JFK, TPA], [TPA, LAX],
cost = fold + 0 (map W E)

The interesting part is how to get the edge list E, and in particular how to write the base cases. Let's call neighbors the function that takes a list and returns a list of neighboring elements.

The case for the empty list:
neighbors [] = []
But what about an argument with a single element? This will work:
neighbors [x] = []
I'm not sure I can explain it better than by saying (1) a graph of one vertex has no neighbors and (2) it makes the case for a two-vertex graph work out.

The general case is then
neighbors (x::y::rest) = [x,y] :: neighbors(y::rest)

We check this with a sample run of the two and three vertex cases:
neighbors [ORD,SJC] =
neighbors(ORD::SJC::(rest=[])) =
neighbors [SJC] = []
neighbors [ORD,SJC] = [ORD,SJC]::[] =
Three vertices:
neighbors [ORD,SJC,AUS] =
neighbors(ORD::SJC::rest=[AUS]) =
neighbors [SJC,AUS] = [[SJC,AUS]] see 2-vertex case
neighbors [ORD,SJC,AUS] = [ORD,SJC]::[[SJC,AUS]] =

Generalizing into an iteration combinator

While I like the decomposition approach, it is easy to understand, it has the performance drawback of creating the intermediate list of pairs. Depending on what gets optimized by the compiler or interpreter, we may want to traverse the list once and simply perform the weight lookup and summing operations. This led me to the idea of fold. Fold itself won't work because it only examines one element at a time, and this function that sums the edge weights needs to examine pairs at a time.

Let's call the function of interest edgeSum. After we apply it to the edge summing, we can easily turn it into a fold-like iteration combinator. Let's say edgeSum needs the following arguments:
  • v - list of vertices

  • w - weighting function that takes a pair of vertices and computes or looks up the weight between them

  • f - the summing function appropriate to the type of the weight (e.g. integer plus or real plus)

  • z - an initial weight, typically zero, the applicable additive identity for the weight type

With this vocabulary, we can define edgeSum:
edgeSum w f z [] = z
edgeSum w f z [x] = z
edgeSum w f z (x::y::rest) = f(w {x,y}, edgeSum w f z (y::rest))
As the function edgeSum is defined (carefully) in general terms, all that remains is to rename it to something like foldNeighbors and we have a general-purpose iteration combinator.

The only refinement we may make is to have foldNeighbors take just one combining function f instead of one that operates on the pair and one that combines the result. This will typically look like fn((x,y),acc)=>body. When implementing edgeSum, we would use f = fn((x,y),acc)=>W(x,y)+acc.

foldNeighbors f z [] = z
foldNeighbors f z [x] = z
foldNeighbors f z (x::y::rest) =
f((x,y), foldNeighbors f z (y::rest))