The Source for Java Technology Collaboration
User: Password:



Rémi Forax's Blog

August 2006 Archives


Syntax of closure

Posted by forax on August 31, 2006 at 06:37 AM | Permalink | Comments (12)

In forum (fora) and blogs, i have often seen that closures are compared to anonymous classes in order to reject the proposed syntax of closure. In this entry, i try to show up why the closure syntax is such as it is by explaining the differences with the anonymous class syntax.

Let me introduce an example that sort a list of integers in reverse order. There are two codes, one written with an anonymous class and the other written with a closure using the closure convertion.

The code using a closure:

List<Integer> list=Arrays.asList(666,7,12);
Collections.sort(list,int(Integer i1,Integer i2) {
  if (i1<i2)
    return 1;
  else
   if (i2<i1)
     return -1;
   return 0;
});

And the code using an anonymous class:

List<Integer> list=Arrays.asList(666,7,12);
Collections.sort(list,new Comparator<Integer>(){
  public int compareTo(Integer i1,Integer i2) {
    if (i1<i2)
      return 1;
    else
      if (i2<i1)
        return -1;
      return 0;
  }
});

I've found five differences and all have a reason so i don't see why people want to change the proposed syntax.

A step by step comparison

  1. An anonymous class starts with new, because it always create a new object, closure can reuse the same singleton so no "new".
  2. an anonymous class takes a type (a class, an abstract class or an interface), closure doesn't need to specify a type because the compiler infers the type. Due to an implementation detail, closure convertion can only occur with an interface.
  3. an anonymous class must parameterize its constructor (Comparator<Integer>). Closure can infer the value of the type variable. Because of the backward compatibility of generics, the compiler must be able to distinguish a raw type from a parametrized type so the type variable of an anonymous class can't be infered.
  4. an anonymous class can take arguments, a closure can't because as state above, a closure can only implements an interface.
  5. an anonymous class need to specify access modifier (public) and the method name. The closure have only one public method so it's neither necessary to name it nor to specify a modifier.

I hope this entry will reconcile the grimpies with the proposed syntax.
Wow, this is my fourth entry about closure, it's time for me to register with CA (Closure Anonymous) :)



Is closure swing ?

Posted by forax on August 29, 2006 at 04:08 PM | Permalink | Comments (9)

Yes, this is another entry about closure :)

In the closure proposal, the section "Closure conversion" describes how to use a closure instead of an anonymous class.
The rules are :

  • the implemented interface must have one method
  • the closure signature must be a subtype of the method signature

So let see if these rules are sufficient to use closures in order to implement AWT/Swing callbacks.

 

Implementing an ActionListener

ActionListener corresponds to a component default action, it's a one method interface, the method actionPerformed takes an ActionEvent that contains among other things the component that received the event.

  public interface ActionListener {
    void actionPerformed(ActionEvent e);
  }

So the client code can be this one :

 JButton button=new JButton("Ok");
 button.addActionListener(() {
   button.setText("Ko");
 });

Oh no !!!
This code is too optimistic, it assumes that because the event is not used, the parameter can be omit. But there is no such rule in the proposal. The correct code is the following :

 JButton button=new JButton("Ok");
 button.addActionListener((ActionEvent notUsed) {
   button.setText("Ko");
 });

The moral

It could be a good idea to add a closure conversion rule that permits parameters omission. Perhaps this rule can be extended to allow the omission of the last parameters from the right.

What do you think about that ?



Function that does not return normally

Posted by forax on August 26, 2006 at 08:04 AM | Permalink | Comments (14)

It's a recurring problem, you have a code that always generates an exception
   public void throwIOException(int code) throws IOException {
     throw new IOException(IO.getMessage(code));
   }
  
and you use it in a method like this :
   public int read() throws IOException {
     int value=readAByte()
     if (value!=-1)
       return value;
     throwIOException(IO.errorCode);
   }
  

Because there is no way to say to the compiler something like "hey, the method throwIOEXception doesn't return", the compiler emit an error because the method read() could return without a value.

In the now famous closure proposal, there is a small section titled "The type of null" that i understand like this: if you use null as return type, you can indicate that the method never return.

   public null throwIOException(int code) throws IOException {
     throw new IOException(getMessage(code));
   }
  

Pretty cool !

update: see Function that does not return normally II (the return) for the part2 of this entry.



Closure and performance

Posted by forax on August 24, 2006 at 05:09 AM | Permalink | Comments (12)

I've read the proposal about function types and closures and i have noticed that the invocation of a function type will use the newly introduced bytecode invokedynamic (the gilad blog about invokedynamic) and i think i know why !

So let me introduce a small example of closure, the method transform applies a transformation on each item of an array and store the result in the same array.
The transformation is specified using a function type typed CharSequence(CharSequence) i.e. a function that takes a CharSequence and returns a CharSequence.

  static CharSequence[] transform(CharSequence(CharSequence) transformation,CharSequence... array) {
    for(int i=0;i<array.length;i++) {
      array[i]=transformation(array[i]);
    }
    return array;
  }

The following code applies an identity transformation :

  CharSequence(CharSequence) identity = (CharSequence seq) {return seq; };
  CharSequence[] seqs=transform(identity,"2","3");
  System.out.println(Arrays.toString(seqs));

So why closure can't use a classical call (invokevirtual) instead of a invokedynamic call, because the proposal defines subtyping rules between closures.
It is possible to use a function type with not exactly the same type the the one defined. In our example, the declared signature is CharSequence(CharSequence seq), one can use instead a closure typed, by example, String(Object).

  String(Object) bang = (Object o) {return "!"+o; };
  CharSequence[] seqs2=transform(bang,"2","3");
  System.out.println(Arrays.toString(seqs2));

So in the method transform, the call to transformation can call a function type with a different signature that the one declared, that's why invokedynamic must be used here.

So why using invokedynamic is important, it's because it can hurt performance, invoking a method without the same signature is more complex than a simple call.
In C#, a delegate method call (a roughly equivalent to closure) is more expensive than a method call.
Note that this is a special case of invokedynamic, there is only one method that is applicable for a specific closure so a smart VM can optimise this case.



Meta protocol mix in JDBC 4.0

Posted by forax on August 02, 2006 at 02:18 AM | Permalink | Comments (6)

While reading the JDBC 4.0 spec, i found a meta protocol mix in the way the spec specifies how to create an empty DataSet using the newly introduced query interface mecanism. But before let me introduce the context :

JDBC 4

JDBC 4.0 introduces a new way to map a class to a result of a JDBC Query automagically. It's not a real O/R Mapper like hibernate or the one describes by the EJB3 spec because by example inheritance or relationship between tables are not managed. It's just a toy O/R Mapper that ease the repetitive task of manually copying data from a ResultSet to object fields.

By example, the class below describes a table of authors and the inner-interface Author.Query describes queries that can be applied on this table.
The queries return a DataSet, it's a new interface that inherits from List (thus is Iterable) and internally stores a ResultSet or a RowSet depending if the query remains or not connected to the database.

public class Author {
  private int id;
  private String firstname;
  private String lastname;
  
  public Author() {
    // used by JDBC mapper
  }

  public int getId() {
    return id;
  }
  
  public String getFirstname() {
    return firstname;
  }
  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }
  
  public String getLastname() {
    return lastname;
  }
  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  public static final String DDL=
    "create table author(\n" +
    "  id        INTEGER NOT NULL\n"+
    "            PRIMARY KEY GENERATED ALWAYS AS IDENTITY\n"+
    "            (START WITH 1, INCREMENT BY 1),\n" +
    "  firstname VARCHAR(128) NOT NULL UNIQUE,\n"+
    "  lastname  VARCHAR(128) NOT NULL UNIQUE"+
    ")";

  public interface Query extends BaseQuery {
    @Select(sql="SELECT * FROM author")
    DataSet getAllAuthors();
    
    @Select(sql="SELECT * FROM author WHERE id = ?1")
    DataSet getAuthorById(int id);
    
    @Update("DELETE FROM author WHERE id = ?1")
    public int delete(int id);
  }
}

To specify a query, the spec introduces two annotations @Select (SELECT)or @Update (UPDATE, ALTER, DELETE etc) but no annotation for operations like CREATE or DROP.
Now, in order to execute a query, the developer must primarily call a new method of the database connection named createQueryObject to create an object that implements the queries interface.
This method prepares the corresponding statements and create reflective data-structures needed to inject data during the execution of a query.
With the query object, all method defined on the query interface are availables, so executing a query is easy as calling a method.

  Connection con=...
  Author.Query query=con.createQueryObject(Author.Query.class);

  DatatSet authors=query.getAllAuthors();
  for(Author author:authors)
    System.out.println(author.getFirstName()+" "+author.getLastname());

[I've updated the following section, Rémi]

Now, the spec introduces a way to create an empty DataSet automatically without using a query SELECT (section 19.4.2.6).
This is done by defining a special method in the query interface. This method need to be named "create" and takes a table name as parameter and returns a DataSet.
Here the spec defines a meta protocol mix !

A meta protocol mix

What is a meta protocol ?
JUnit uses a meta protocol to defined its tests, a method is a test method if its name starts with "test". It's a way to segregate classical methods from the ones that play a special role.
Using annotations is another way to defined a meta protocol but in the case of the current JDBC 4 spec, the spec mix a meta protocol defined with annotations and a one defined with method name.

What if a method named "create" is tagged by an annotation @Select ? oups, there is a meta protocol mix.
I don't know if is's too late to modify the spec but i think the spec needs some clarification in that area.





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