Brian Slesinsky's Weblog
 
   

Tuesday, 29 May 2007

Guice with Curry

I previously explained how Guice works, but not how it changes your application's archiecture when you use it. Now that I've had some practical experience, I can explain a little more about that.

While refactoring my 20% project to use Guice for creating objects, I found myself adding factory classes where previously the code would call constructors directly. This seemed odd because up until now, I've generally thought that factories are overused and best avoided. Later in this article I'll explain why this happens. But first, a digression into functional programming languages...

Currying in Haskell

A nice feature of functional programming languages such as Haskell is that they support curried functions. For example, let's define an (admittedly trivial) function in Haskell to multiply two integers:

   product :: Integer -> Integer -> Integer
   product a b = a * b

The first line declares the type of the product function. (This looks a little weird and I'll get to that in a minute.) The second line defines its implementation. Here is how you would try out this function from a Haskell interpreter's command line:

   > product 6 7
   42

Haskell's syntax for functions looks rather strange to most programmers: where are the parentheses? Actually, we could have put parentheses around product's two parameters, but leaving them out tells Haskell that the function is a curried function.

Curried functions have the property that you can call them with fewer arguments than they're declared to take, and the result is another function that takes the remaining arguments. Here are some examples:

  double = product 2
 
  > double 6
  12
  > double 7
  14
 
  triple = product 3
 
  > triple 2
  6

This trick works because a curried function such as product is not actually a two-argument function. It's really a function that takes one argument and returns a function that takes a second argument, returning the answer. That explains the funny type declaration:

  Integer -> Integer -> Integer

For a non-curried function, the type declaration would look like this:

  (Integer, Integer) -> Integer
Back to Java

So, that's a neat trick, but what does it have to do with Guice?

Suppose you are writing a Java class that can play a song. A first attempt might look like this:

public class Song {
  public Song(SongPlayer player, String name, File mp3) {
    ...
  }
 
  public void play() {
    player.play(mp3);
  }
}

This code seems perfectly reasonable when you're writing constructor calls by hand. It's a little awkward to have to pass three arguments, but the play method requires a player, so it seems like there's no help for it. (Yes, there are other ways, like using static variables, thread locals, or singletons, but they seem worse after doing test-first development for a while.)

Let's say that in our application, the SongPlayer is configured at startup and never changes, but each song has a different name and mp3 file. So, we would like to Guice to inject SongPlayer while leaving the other two arguments alone.

It might seem that a Provider is the answer to everything in Guice, but in this case, the best option is to write a factory:

public class Song {
  protected Song(SongPlayer player, String name, File mp3) {
    ...
  }
 
  public static class Factory {
    private final SongPlayer player;
 
    @Inject
    public Factory(SongPlayer player) {
      this.player = player;
    }
 
    public makeSong(String name, File mp3) {
      return new Song(player, name, mp3);
    }
  }
}

Now we can inject Song.Factory into any class that needs to create songs, and they will be able to do it without knowing anything about SongPlayer.

I found myself applying this refactoring repeatedly while converting my web app to Guice. Eventually it occurred to me that all I'm really doing is currying the Song constructor!

That is, moving from this:

  Song song = new Song(player, name, mp3);

To this:

  Song song = new Song.Factory(player).makeSong(name, mp3);

Is analogous to removing the parentheses on a function in Haskell, except that there is a lot more typing involved.

In general, you need to curry a class constructor whenever some of its parameters are shared among all instances (and can be hidden) while others contain instance-specific data (and need to be exposed). This is a "code smell" that's largely invisible to programmers who haven't done much dependency injection, but it's not hard to write the code that way the first time, once you know what to look for.

How often you need a factory depends on your coding style. It's also possible to use a service-oriented architecture for Song and SongPlayer:

  public class Song {
    public Song(String name, File mp3) {
      ...
    }
 
    void play(SongPlayer player) {
      player.play(mp3);
    }
  }

In this alternate design, any code that constructs songs can call the Song constructor directly, but classes that want to play songs need to be injected with a SongPlayer. As a result, no currying is needed.

Each of these designs has its advantages. The service-oriented design is admittedly more configurable, since each call to play could use a different song player. If you have lots of songs, it might also save memory because Song instances are smaller.

However, I prefer writing smart objects that have high-level methods such as play that take few or no arguments. Songs that play themselves might seem like a strange way to model the world, but I find that reducing the number of arguments to a method tends to make it easier to use.

Can we do better?

Apparently, the price for this when using Guice is that you have to write lots of factories. But these factories are simply boilerplate. It would be nice if a future version of Guice would just write them for us. Here's a sketch of how Song might look when written using some future version of Guice:

  public class Song {
    @Curried
    protected Song(@Inject SongPlayer player, String name, File mp3) {
      ...
    }  
 
  }
 
  public interface SongFactory {
    Song makeSong(String name, File mp3);
  }
 
  public class MyModule extends AbstractModule {
    public void configure() {
      factory(SongFactory.class).builds(Song.class);
    }
  }

In the meantime, writing factories yourself is still pretty clean and the overall results of adopting Guice are worth it. I had thought my code was well-organized before, but adopting Guice made it singificantly cleaner. I expect that Guice will become one of the libraries that I inevitably add to any web application I write, along with Jetty, JUnit and EasyMock.

[Update as of July 2007: since this article was written, a couple of folks at Google wrote the AssistedInject extension to do exactly this, though with a different syntax. Also, there are plans to add it to Guice; see Issue 131 for updates.]