Blocks Inside Out

In my previous post I gave some practical applications of blocks (a.k.a closures) in Gosu. In this post I’m going to look at one particular method mentioned in that article, where(), and try to show how the concept of blocks emerges from code you might be writing today. I hope by the end of it you’ll understand what I mean when I say that:

    “Classes help us abstract concepts, blocks help us abstract algorithms”

As a refresher, where() is a method in Gosu available on anything implementing Iterable<T>. It takes a single block and returns a new sublist of items where the block returns true.

Now, backing up a step, let’s look at some code that filters a list written in vanilla Java:

  List<String> shortStrings = new ArrayList<String>();
  for( String s : aListOfStrings ) {
    if( s.length() < 10 ) {
      shortStrings.add( s );
    }
  }
  return shortStrings;

Very standard Java code. I want to focus on the bolded part of the algorithm: it’s a little bit of logic that actually provides the filter. Note that it is very specific to this particular application of the filter algorithm: this bit of logic is deeply baked into the for-loop. This makes it difficult to extract the code around it, which is all fairly general, into a reusable bit of software.

Now, if filtering lists of strings on length was common enough in your code, you might extract this logic into a function and make the maximum (+1) length of the strings a parameter:

  List<String> findStringsLessThanLength( List<String> aListOfStrings, int length ) {
    List<String> shortStrings = new ArrayList<String>();
    for( String s : aListOfStrings ) {
      if( s.length() < length ) {
        shortStrings.add( s );
      }
    }
    return shortStrings;
  }

This has generalized the operation a bit: you now can pass in varying lengths and get different results. However you are still stuck with the fact that this method can only filter strings with a length less than some number. If, for example, you wanted strings greater than some length, you’d need to either introduce another method or, yikes, introduce another parameter.

Ugly.

At this point, depending on how crazy you are willing to get and how much you hate copy and pasting the same algorithm everywhere, you may choose to introduce a Predicate concept to your project. This allows you to pass an anonymous inner class to a method, where the anonymous inner class expresses the filter critera:

  return Iterables.filter( aListOfStrings, new Predicate<String>() {
    public boolean apply( String s ) {
      return s.length() < length;
    }
  }

Hmmm. Well, some people might think that’s better. I’m not so sure.

The problem (in my eyes) is that the amount of syntax overwhelms the core concept: we are just filtering a list, for goodness’ sake.

Looking back at the original implementation:

  List<String> shortStrings = new ArrayList<String>();
  for( String s : aListOfStrings ) {
    if( s.length() < 10 ) {
      shortStrings.add( s );
    }
  }
  return shortStrings;

It’s really just that bolded part we care about. Wouldn’t it be nice if we could pass just that to a function?

That’s exactly what blocks let you do:

   return aListOfStrings.where( \ s -> s.length() < 10 )

The block (again, the funny bit after the ‘\‘) has a single parameter, a string named ‘s‘, and returns true if s has a length less than 10, false otherwise. You can think of the block as a mini-function, if that helps, or as an mini-anonymous-inner-class. All blocks do is take arguments and return values, in an conveniently-inlineable syntax. And, of course, you write whatever expression you’d like in the bolded area, so this is a truly general solution to filtering things.

That’s what I mean when I say that blocks help you abstract algorithms: ideally, you’ll never have to write that filtering code ever again. Or that sorting, mapping or partitioning code either.

Sweet.



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s