Showing posts with label java closures. Show all posts
Showing posts with label java closures. Show all posts

Wednesday, October 31, 2007

Searching Jars with Java Closures

(Ed's note: see this post for a better example of closures. This one is intended for direct contrast with a Groovy script: the resulting Java is awkward.)

The last post has a "Hello World", of sorts, for Neal Gafter's closure proposal.

In it, I mentioned that I have converted a Groovy jar searcher into pure Java, using Neal Gafter's prototype.

Here is the code. It is probably best to understand the previous post before looking into this. I may also be instructive to understand the Groovy code first.

The code is documented but here is a view from space:

  • The program takes a directory and a target string as arguments. It searches all jars in the directory for the target string, and reports the number of matches found.
  • A use-case is that you are looking for MyMissingClass in a bunch of jar files.
  • The program uses 2 closures as private members to the class.
  • One closure is myEntryChecker, which accepts a JarEntry and uses the target variable as a 'free lexical binding'. It returns a boolean if the name of the JarEntry contains the target string.
  • The other closure is myFileChecker, which accepts a File and uses the above closure as a 'free lexical binding'. Note that this one declares an IOException.
  • Note that there is a cool proposal for loop abstractions that is not yet implemented. This will make run() much more elegant. In fact, the invocation of closures seems to be a major motivation for the proposal.
(Btw, if you find the example less-than-elegant, blame me, not closures. The intent is not to introduce closures, per se, but instead to illustrate the new Java syntax by leveraging a simple-yet-useful program implemented in Groovy.)


import java.io.*;
import java.util.jar.*;
import java.util.Enumeration;

public class JarSearcher {

private String target;
private String fileName;

// This closure checks a JarEntry's name for a target string
// @param jarEntry
// @return boolean true if found
// @free target the string we're looking for
// ('free' is my way of denoting a free variable)

private { JarEntry => boolean } myEntryChecker = {
JarEntry jarEntry =>
// expression! no return statement or semi-colon
// Thanks to Christian Ullenboom for the simpler version
jarEntry.getName().contains(target)
};

// This closure iterates over the entries in a jar file and
// checks each one for a target string (via another closure)
// @param file
// @return int # of occurences
// @throws IOException
// @free myEntryChecker
// ('free' is my way of denoting a free variable)

private { File => int throws IOException } myFileChecker = {
File file =>
int count = 0;

fileName = file.getName();

if( fileName.indexOf(".jar") != -1 ) {

JarFile jarFile = new JarFile(file);

// old-style Enumeration
// don't blame me or closures!
for( Enumeration e = jarFile.entries() ;
e.hasMoreElements() ; ) {
JarEntry entry = (JarEntry) e.nextElement();
if( myEntryChecker.invoke(entry) ) {
count++;
}
}
}

// expression! no return statement
count
};

// This method lists the files in a directory and
// uses a closure to check each file for a target
// string (if it is a jar file)
// @param args[]
// @throws IOException (because the closure declares it)

public void run(String[] args) throws IOException {
String searchDirStr = args[0];
target = args[1];

File searchDir = new File(searchDirStr);

// NOTE: Neal has a proposal that would make this
// iteration much easier
for(File file : searchDir.listFiles() ) {
int count = myFileChecker.invoke(file);

if( count != 0 ) {
System.out.println(
"found " + count + " match(es) in " + fileName );
}
}
}

public static void main(String[] args) {
try {
JarSearcher jarSearcher = new JarSearcher();
jarSearcher.run(args);
} catch(Exception ex) {
// TODO
}
}
}

Tuesday, October 30, 2007

Java Closures: A Quick Look


Neal Gafter is back! And he has brought treats for Halloween by way of a prototype on the closure proposal.

Essentially, it's a binary version of a modified JDK 6, released under the Java Research License. If Java is our home, Neal is building a whole new section, with a deck and swimming pool.

As a first exercise, I have converted my modest Groovy jar searcher into pure closure-Java. While doing the research, I realized:

  • Syntax is like construction. You can watch both all day but it's when you get your hands dirty and pick up a tool, that things begin to click.
  • Much like generics, closures will bring a lot of new terms and concepts. I don't mean the notion of a closure, per se, but rather the implementation in Java.
  • Already there is a lot of discussion: most of it from hardcore syntax surfers who revel in the corner cases and possibilities. (And more power to 'em)
The upshot is: I wonder if there is an opening in the blogtomonous collective for a gentler introduction. Even easier (and quicker) than the Groovy example mentioned above.

Here's a quick look....

The Setup
  • The download is available via Neal's original post Be sure to read the JRL
  • If you're on Windows, you might want a tool like 7-Zip to open the ztar file
  • Define CLOSURES to be the main directory
  • Ensure that JDK 6 is in the path. The shell files in CLOSURES/bin are key, as they modify boot classpath to use the new stuff. Though I love Bash on Windows, I used straight Windows BAT files that were morphed from javac and java in ~/bin.
The Gist

I'll assume that you're familiar with closures from Groovy, Ruby, Scheme, etc. With respect to the Java syntax, the most important things to understand are the Closure Literal and the Function Type.

In essence, the Closure Literal is the closure body; a Function Type is an abstract data type, a generalization of the venerable T for Generics. The difference is that the Function Type specifies many types for a function: (1) the types of the parameters, (2) the return type, and (3) possible exceptions.

I know: ZZZzzzz. To the ExampleCave, Batman!


// SNIP: public static void main...

String bulletin = " Hello Closure! ";

// Below, think: Type varName = closure;
//
// where Type = Function Type = { String, String => String }
// varName = myEasyClosure
// closure = Closure Literal (i.e. _the_ closure)

{ String, String => String } myEasyClosure =
{ String a, String b => a + bulletin + b };

// The closure is built into a class with an invoke method.
// To use it, simply call invoke with appropriate parameters.

String s = "this ";
String t = " is cool";

String result = myEasyClosure.invoke( s, t );

// output:
this Hello Closure! is cool

As annotated above:
  • We have a closure called myEasyClosure
  • Just as a generic type gives us, well, the type of a generic class, the Function Type states the types of the entire closure. Gafter could ask to us write IT_TAKES_2_STRINGS_AND_RETURNS_A_STRING but mercifully we write { String, String => String }. It is vital to understand that syntax.
  • The Closure Literal echoes the syntax of the Function Type: { params => expression }. Note that it doesn't return anything.
  • The whole point of closures are free lexical bindings: e.g. the free bulletin variable. Though this example is silly, that's where things get cool.
Keen readers will note that Function Types also specify exceptions. Let's decide that our closure should check for null on the parameters, and throw an IllegalArgumentException if that contract is violated.

Here's how to express it:


// remember:
// FunctionType varName = closure;

{ String, String => String throws IllegalArgumentException }
myEasyClosure =
{
String a, String b =>

if( a == null || b == null ) {
throw new IllegalArgumentException
("null parameters not allowed");
}

a + bulletin + b // no return or semi-colon !
};
Two points here:
  • The Function Type looks complicated, but we simply added a throws IllegalArgumentException into the definition.
  • In both examples, note that the final part of the closure body is an expression.
That's it... It should be fairly easy to mock up these examples with the prototype. With that foothold, it is easier to understand the more complex examples and discussions.