The Source for Java Technology Collaboration
User: Password:



Bruce Chapman's Blog

Programming Archives


List Separators

Posted by brucechapman on March 25, 2008 at 05:58 PM | Permalink | Comments (12)

In his Disturbing Thoughts from a Developing Mind blog, fellow kiwi Mark Derricutt discusses a situation where new for loops don't provide enough power for a particular case. (And yes this blog has been sitting drafted but unpublished for ages :( )

The case in point is building a String from the concatenation of a List of Strings with some separator between them, in this case a semi-colon.

The problem is expressed as "append the separator after each element, except the last one". What seems to have evaded Mark and his commenters is that the problem can be expressed differently.

If you restate the problem as "append the separator before each item except the first one" then you are one step away from what seems to me to be an elegant solution, at least compared to the others offered.

That final step is to realise that "except the first one" can be rephrased as except the first one in which case you prepend nothing".

Applying this to the relevant section of Mark's code yields

StringBuilder sb = new StringBuilder();
String separator = "";
for (String listenerClassName : data.TEST_LISTENERS) {
    if (listenerClassName != null && !"".equals(listenerClassName)) {
        sb.append(separator).append(listenerClassName);
        separator = ";";
    }
}

There is a more OOish solution, which is to have a (tiny) class thus

public Class Separator {
    private String next = "";
    private String separator;
    public Separator(String separator) {
        this.separator = separator;
    }

    public String get() {
        String result = next;
        next = separator;
        return result;
    }
}

which returns an empty string the first time get() is called, then the configured value thereafter.

With this we can code the loop as

StringBuilder sb = new StringBuilder();
Separator sep = new Separator(";");
for (String listenerClassName : data.TEST_LISTENERS) {
    if (listenerClassName != null && !"".equals(listenerClassName)) {
        sb.append(sep.get()).append(listenerClassName);
    }
}

So how do you tackle this problem?



Announcement - "No Closures" prototype

Posted by brucechapman on March 08, 2008 at 01:39 AM | Permalink | Comments (12)

All the major closures proposals now have prototype implementations available. But until we can play with the final option "No Closures" we're not in the best position to make a good decision. So it is with pleasure that I announce the availability of a prototype for "No Closures".

With this jar file and JDK 6, you can effectively cast a method to a SAM type. For those not following the various closures proposals too closely, a SAM type is a "Single Abstract Method" type. That is, a class or an interface with only one method that is abstract.

So to cast a method to a SAM type, the method should have the same argument types and return type as the SAM, but it can have any name. It may NOT be private. You then annotate that method with @net.java.dev.rapt.anon.As, and specify the SAM type.

Here is a method declared to be castable as a Runnable

import net.java.dev.rapt.anon.As;

class MyClass {
    @As(Runnable.class)
    void slowStuff() {
        ... // whatever
    }
}
In order to cast that method to a Runnable, you do this
new Thread(Runnables.slowStuff(this)).start();
Thats right, you get the Runnable by calling a static method on the Runnables class, and pass the object that owns the method.

So how does this work?

In the jar file containing the annotation there is also an annotation processor. For each SAM type with at least one corresponding @As annotation in a package, it creates a class in that package whose name is the SAM type name with an "s" appended, hence Runnable SAM type -> Runnables.java created. That class has static methods which make anonymous inner classes that delegate to each of the methods in that package annotated with @As(Runnable.class)

Those generated static methods look like this

class Runnables {
    private Runnables() {}

    static Runnable slowStuff(final MyClass owner) {
        return new Runnable() {
            public void run() {
                owner.slowStuff();
            }
        };
    }
}

If you want to pass additional arguments to the method which are not part of the SAM, just annotate those with @As.Additional, and when you call the generated class, pass those arguments.

Example

Here is a small swing app that demonstrates things.

package asdemo;

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import net.java.dev.rapt.anon.As;

public class Main extends JFrame {

    JButton goButton;
    JLabel  label;
    
    public Main() {
        super("@As");
    }

    public static void main(String[] args) throws Exception {
        Main instance = new Main();
        // run instance's startUp() method on the EDT
        EventQueue.invokeAndWait(Runnables.startUp(instance));
    }
    
    @As(Runnable.class)
    void startUp() {
        setLayout(new GridLayout(2,1));
        goButton = new JButton("Go");
        goButton.addActionListener(ActionListeners.onGo(this));
        label = new JLabel("Gone");
        add(label);
        add(goButton);
        pack();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    
    @As(ActionListener.class) // ActionListener for goButton
    void onGo(ActionEvent e) {
        goButton.setEnabled(false);
        // run goBackground() in a background thread
        new Thread(Runnables.goBackground(this)).start();
    }
    
    @As(Runnable.class)
    void goBackground() {
        for(int i = 10; i >= 0; i--) {
            try {
                // call showStatus(i) on EDT 
                EventQueue.invokeAndWait(Runnables.showStatus(this, i));
                Thread.sleep(500);
            } catch (InterruptedException ie) {
                return;
            } catch(InvocationTargetException ite) {
                 ite.printStackTrace();   
            }
        }
    }
    
    @As(Runnable.class)
    void showStatus(@As.Additional int value) {
        if(value != 0) {
            label.setText("Going " + value);
        } else {
            label.setText("Gone");
            goButton.setEnabled(true);
        }
    }
}

This should compile with JDK 6. You will find the generated Runnables and ActionListeners classes whereever it is you tell javac to place generated classes (the -s option if used, otherwise in the same place as the compiled .class files).

What's Hot and What's Not

This is designed to be work correctly even if you do incremental compiles. The processor remembers all the SAMs for the project and keeps them up to date, even if you only compile classes one by one.

Currently this doesn't work for generic SAM types. That means you can't use it to cast a method int compareStringLengths(String s1, String s2) to a Comparator<String>. I am working on a version that will infer the type parameters and the SAM type returned from the generated class will have the correct type parameters, but that isn't ready yet.

I don't think this is as good as any of the closures proposals, except in one respect, you don't have to wait for JDK 7 to use it.



BGGA FUD Busting - Part 1

Posted by brucechapman on February 27, 2008 at 12:46 AM | Permalink | Comments (5)

Josh Bloch makes some interesting points in his Javapolis presentation on the closures controversy. However having listened to the audio several times and read the slides at least three times, I am having trouble extracting the salient points from all the FUD

(The presentation video of the talk is now also available)

What seems to be happening is that much of that FUD is starting to spread.

In this post I want to expose one of these items of FUD to some light and see how it really looks. Also I am hoping that having neutralised the noise, I might more easily extract some signal from Josh's talk. I think its there but I can't hear it just yet.

At 13:06 into the sound track, Josh starts comparing interfaces to function types by first refering to these javadocs for an interface and talks about it having a name, all known implementations, and documentation including semantic constraints. He then says

"with functional types all you get is 'it takes two T's and gives you a T'. There's a lot less information there."

This presumably is in reference to where the types are used. Compare this version of a method in the fork join library with this one using closures.

An Ops.Reducer is of no value except where it is used. Now with the non closures version, all we see in the javadoc for the reduce() method is the names of the type and parameter, whereas with closures we see the essential method signature, because that is the closure type, and the parameter name. The closure version tells me more because all I get without the closure is a type name and a parameter name, and if I want to know more I have to click a link to go find it. With the closure I don't ned to first follow the link.

Recently Stephen Colbourne tried to give more impetus to this FUD when he said

"A second complaint about function types is that there is no home for documentation. One of Java's key strengths is its documentation capabilities in the form of Javadoc."

To date I haven't seen anyone describe how the javadoc tool would work for closures, so let me suggest that when a method return type is a function type, or a method parameter is a function type, then the return and parameters of that closure type will be able to have nested javadoc associated with them.

I would expect you'd find the javadoc for ParallelArray.reduce () being something like


reduce

public T reduce({T,T=>T} reducer,
                T base)
Returns reduction of elements

Parameters:
reducer - the reducer. This function must be associative.
reducer returns
The result of combining the two arguments in some way
reducer parameter 1
The first argument to be combined.
reducer parameter 2
The second argument to be combined.
base - the result for an empty array
Returns:
reduction

You could do that now, but some new javadoc tags to do the nested documentation would make life simpler.

For sure those are pretty terrible javadocs, I just made them up, but this demonstrates that the function type's javadocs can be just as rich as the nominal type's.

Before you say it would get tedious writing out the same complicated javadoc at every place where you used a particular function type, let me say that the point at which it gets painful is probably the point at which you should consider using an interface rather than a function type, and the javadoc pain is your friend by helping you to see that.

And one last thing, Josh says that with an interface, you can find all the known implementations from the javadoc. Not so, you won't find the implementations that are anonymous inner classes. And those are exactly the sorts of places where you would use nominal types and closures. So that's not really an advantage of nominal types over function types in the places where you'd actually use them, it's just more FUD.



Luddites - Watch Your Back

Posted by brucechapman on December 17, 2007 at 01:54 PM | Permalink | Comments (5)

Neal Gafter's latest blog on closures has attracted two or three comments from people wanting to object to generics, and a number of other conservative commenters voicing an objection to closures. Josh's JavaPolis presentation makes a valid point that these opinions cannot be ignored.

I have invested some time getting my head around some (but not all) of the closures proposals. I don't know whether I find that easier or harder than your average practitioner, but with a little effort I can now relatively easily read and comprehend most example code.

I have also invested some time getting my head around the early draft of JSR-308 and there are parts (like method receivers) of that which I just can't get my head around. Maybe I'm thick, or maybe the spec does not do a good job of explaining what is fundamentally a good idea, or maybe the idea is fundamentally wrong. I don't yet know.

What I do know is that closures is not the only language proposal out there that can make a java program look foreign to those who can't or won't put in the effort required to internalize them. JSR-308 makes closures look simple, and its a lot closer, in terms of the JSR lifecycle, to becoming a part of the java language specification.

To all those who are voicing opinions against language changes that make java more complex, have you read the JSR-308 early draft? And have you sent your comments to the expert group?





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds