Home > Groovy > Chain those Crazy Groovy Closures with Currying, Case Study: Mimicking Intercepting Filter

Chain those Crazy Groovy Closures with Currying, Case Study: Mimicking Intercepting Filter

Below is a post I wrote a little while back but never published. If you’re wanting to learn better how closures work or how to use currying, this can be a really fun exercise. I didn’t publish this earlier because I didn’t want to encourage anyone to implement this in production code as if this were some kind of pattern.
Please, treat this post merely as something fun to play with. Do not use this in production code. Please, I beg you. Think of the kittens.
– Peabody
A week ago Josh and I wanted to chain a bunch of behaviors together in Groovy.
One of the first possible solutions that popped to mind was the very flexible Intercepting Filter pattern common to Java EE web applications.
I think we ended up deciding, for what we actually needed, this neato solution wasn’t the best fit. But the process was extremely rewarding and sharing it might help some folks get a better feel for how powerful closures can be and get a little more familiar with currying.
Here’s the basic pattern diagram for the Intercepting Filter from Sun’s site. The important parts here are the Filters One, Two, and Three. We want to be able to call a chain of these filters and each filter will be responsible for doing any pre or post processing and likely call the next filter in the chain at some point in the middle.
Figure 7.1
Here are our three filters. They’re simply closures that accept another closure as an argument. In JavaEE, the Intercepting Filter gets added to a chain of other filters. Each one can run some pre-processing logic, call the next item in the chain, then call some post-processing logic before returning. In our closures, the pre and post processing both simply output something to the console and, in between those, we call the next item in line with c.call(). The c={} argument defaults our c variable to an empty closure if no other closure is passed in as an argument. 

def one = { c={}->
  println "one*";    c.call();  println "*one"
def two = { c={}->
  println "two*";    c.call();  println "*two"
def three = { c={}->
  println "three*";  c.call();  println "*three"

Note to Reader: Please copy this code into your GroovyConsole as you follow along. It will be much, much more fun for you that way. ;)
So now I can call one of my fancy new closures like I would a method.
Try this:
I called the two closure with no parameters. This worked even though the signature for two has an expected variable of c. It did not complain because I declared it c={} which means that it will just default to an empty closure if I don’t supply one.
Let’s call our two closure again now but supply it with another closure as an argument.
Try this:
two{ println ‘hey there!’ }
hey there!
That worked pretty well. Now let’s try combining the closures.
Try this:
That’s pretty sweet. But we might end up needing a handful of behaviors to run through and that curly brace syntax can become messy quickly.
We know that with a little currying we can build all of my little closures into a bigger closure.
def myChain = one.curry(two.curry(three.curry({})))
Then we can call that one chain by itself.
But we want to dynamically build that chain. We should be able to call it like:
chain([one, two, three])
Or if we had more closures:
That allows us to pack a lot of behavior into a small amount of code and we can mix, match, and put these behaviors in any order we want.
Here’s a little method to help us with that.
def chain(List commands){
  def chainToCall = {}
    chainToCall = command.curry(chainToCall)
Give it a try! :)
PS – Here is an even shorter version of that chain method by using inject. One of my next posts to Inside the Machine will explain inject in greater detail. I hope you can see that inject allows much more succinct code.
def chain(List commands){
  commands.reverse().inject({}){chainToCall, command->
  1. Garrett Rowe
    November 13, 2008 at 2:31 pm

    I’m guessing inject() in Groovy is is equivalent to foldr in Haskell or foldRight in Scala.

  2. Vinay
    November 25, 2008 at 8:43 pm

    chainToCall = it.curry(chain)

    Should above libe be chainToCall = it.curry(chainToCall)?

    if not what is chain?

  3. November 26, 2008 at 6:35 pm

    @Garrett – inject() in Groovy is exactly that. I believe the name inject was copied from Ruby and I think in some languages it’s called reduce. I’m not a big fan of the name “inject” but what it does is really neat.

    @Vinay – Thanks for the compiler catch! I did mean for it to be chainToCall like you said and I updated the post to reflect that. That’s what I get for changing variable names inside of wordpress! ;) Thanks again!

  4. Chris
    December 19, 2008 at 6:09 pm

    More interesting is when the chained functions take parameters, and pass values to the next:

    f1 = { x -> x * 2 }
    f2 = { x -> x + 1 }

    def chain(List l) {
    {p -> l.reverse().inject(p, {r,f -> f(r)}) }
    c = chain([f1,f2])

    This builds a closure containing the list of functions, and evaluates it with inject (reduce) when called with a value. There’s no need to use curry, which coupled with inject would build a needlessly complicated sequence of closures. I use this approach (in Java) to build transducer pipelines for machine learning.

  5. baris k
    January 22, 2009 at 12:16 pm

    still waiting for that detailed inject explanation

  6. January 23, 2009 at 9:02 am

    Thanks for the reminder, baris k. The Columbus Ruby Brigade this Monday had a short presentation on Haskell that included the foldl function, which naturally the Rubyists could identify with through their use of inject.

    I promise that this and painting our new baby room are my only two priorities for the weekend. Should be doable.

  1. January 25, 2009 at 10:44 pm
  2. December 31, 2009 at 3:17 pm

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 )

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s

%d bloggers like this: