FeatureRequest: @Closure

145 views
Skip to first unread message

eric.giese

unread,
May 6, 2013, 1:06:55 PM5/6/13
to project...@googlegroups.com
What you are asking for is part of the experimental lombok-pg which has lot of great experimental ideas (like the adapted @extensionmethod).

https://github.com/peichhorn/lombok-pg/wiki/%40Function

Phillips idea had one disadvantage: runtime dependencies on function objects, but they are easily remedied in a way very similar to your suggestion.
In combination with guava this is a very good idea if there is also a way to turn getters into static functions (imho the most important usecase).

The question is if the inclusion is worth it, considering it will be relevant for such a short amount of time.
Java8 will make it irrelevant when it is matured and arrives at the mainstream, i say starting at 2015.
Thats just one and a half years. Not very long for a library feature.
Maybe as a permanent part of the experimentals?

Anyway, i would also like and use it until the dark age of java is finally over :-)

Reinier Zwitserloot

unread,
May 6, 2013, 3:02:25 PM5/6/13
to project...@googlegroups.com
This looks really cool, but it requires some more thought in the exact spec of this proposed feature, and unlike @Cleanup I think whatever we can come up with will be a pale shell of what JDK8 will bring.

So, before this feature is actually stable, JDK8 will be on our doorstep. I get that as a lombok feature it would work in JDK7 and JDK6 too, but that's not really worth it I think.



On Friday, May 3, 2013 2:46:26 PM UTC+2, Marcio Aguiar wrote:
Is it possible to annotate a method so it can act as a closure? For instance:

@Closure(Runnable.class)
public void doSomething() {
    ....
}

will turn into

public class doSomething implements Runnable {
}

or

@Closure(Callable.class)
public Person doThat() {
  ...
}

would turn into

public class doThat implements Callable<Person> {
   public Person call() {
   }
}

and finally

@Closure(Function.class)  // guava
public Team getTeamOf(Person p)

into

public class getTeamOf implements Function<Person, Team> {
}

This way i could write Collections2.transform(collection, new getTeamOf());

Marcio Aguiar

unread,
May 6, 2013, 9:10:17 PM5/6/13
to project...@googlegroups.com
Thanks, guys! It makes sense.

I just found out about lombok-pg today! I wonder why don't you advertise it more on your site. Lombok is so cool that I hoped the web would be filled with extensions. :)

Anyway, I will try some annotations avaiable at lombok-pg. I was just taking a look on how it all works and I'm looking forward to contribute.

eric.giese

unread,
May 9, 2013, 2:11:15 PM5/9/13
to project...@googlegroups.com
You might also want to have a look at funcito:
https://code.google.com/p/funcito/

This creates guavas functions and predicates with dynamic proxies.
Its syntax is very similar to the popular mockito, hence the name funcito,
and it seems to be a super lightweight api.

I will try it out soon, it seems to be promising.

eric.giese

unread,
May 11, 2013, 4:51:09 AM5/11/13
to project...@googlegroups.com
Hm. Funcito is less useless than I thought.
Needs a Bytecode-Generator like cglib and might not be fully error-proof.

This day I thought about a useful spec for @Closure.
Even though the feature will be useless as soon as Java8 comes around, the long adoption times in many projects might make an intermediate solution worthwhile.

Syntax:
@Closure(value=Class<T>)
Class must be an interface with exactly one abstract method.
Abstract classes aren't needed. Java8 also does not allow them for lamdbas)
Can be applied to static methods, methods, static fields and fields.

What it does:
It will create an instance of class with the following implementation for its abstract method.
Parameters:
- If the annotated element is an instance method or field, the first parameter is the object instance to which the field/method belongs
- If the annotated element is a function, all following parameters are applied as-is.
Returns
- If the annotated element is a method with a void type, then the return type is void
- If the annotated elment is a field or a method with a non-void return type, then this is the return type
Types
- Generic Types will be applied to the function and field types.
- Autoboxing to primitives applies in both directions, with the usual null-pointer exceptions if it fails.
- All other types must match exactly. So an String-Field cannot be used for a Closure-Type which returns a (non-generic) Object.
Exceptions
- If the annotated element is a method, then its throws-clause must be throwable by the closure class.
This restriction is not needed, but it might make the implementation easier.

Result:
The Result of the Function is a public static final Field of the value-Type of the annotation with the Name converted to UPPERCASE_UNDERSCORE-Notation based on the Camelcases AND the name of the closure-class.
So getName for a Function becomes GET_NAME_FUNCTION.
If the field exists, the javac should emit the error for a duplicate field.

Examples:

import static com.google.common.base.*;

class XType{

  @Closure(Function.class)
  private String name;

  @Closure(Function.class)
  public String getName();

  @Closure(Predicate.class)
  public static boolean calculate(int x);

  @Closure(Comparator.class)
  public int compareTo(String other);

  @Closure(Supplier.class)
  public static final int NAME_LENGTH;

  @Closure(Callable.class)
  public static InputStream open() throws IOException;
}

Results in:

class XType{

  public static final Function<XType,String> NAME_FUNCTION = new Function<XType,String>(){ public String apply(XType input){input.name);} };
  private String name;

  public static final Function<XType,String> GET_NAME_FUNCTION = new Function<XType,String>(){ public String apply(XType input){input.getName();} };
  public String getName();

  public static final Predicate<Integer> CALCULATE_PREDICATE = new Predicate<Integer>(){ public boolean apply(Integer input){calculate(input);} };
  public static boolean calculate(int x);

 
  public static final Comparator<String> COMPARE_TO_COMPARATOR = new Comparator(){ public int compare(Person o1, Person o2){o1.compareTo(o2); };
  public int compareTo(Person other);

  public static final Supplier<Integer> NAME_LENGTH_SUPPLIER = new Supplier<Integer>(){ public Integer get(){return NAME_LENGTH;} };
  public static final int NAME_LENGTH;

  public static final Callable<InputStream> OPEN_CALLABLE = new Callable<InputStream>(){public InputStream call() throws IOException{return open();} };
  public static InputStream open() throws IOException;
}

Is such a transformation possible in a trivial to implement way?
While there are a lot of rules, most of them should be straight-forward.

Reinier Zwitserloot

unread,
May 16, 2013, 5:31:17 PM5/16/13
to project...@googlegroups.com
Why can't things be instance methods here?

At any rate, this means we need to do multi-level resolution on the type specified, and it can't have generics (or we'd have to try and back those out from the parameters / field types of the thing you annotated, which is complicated).

So, complex. Thus, not happening before java8 comes out. Probably. Hey, it's oracle, java8 will probably be out somewhere around 2092.

Marcio Aguiar

unread,
May 28, 2013, 11:11:11 PM5/28/13
to project...@googlegroups.com
Nice, eric!

I started messing aroung lomboks code and I it's possible.

I came up with a Builder and a Comparable annotation. I'm moving to implementing @Closure now.

eric.giese

unread,
Jun 5, 2013, 7:29:41 AM6/5/13
to project...@googlegroups.com
FYI, for now I have solved this problem by implementing some helper classes with same behavior via generic-type-capture and reflection:

@Data
class Person {
    private final String name;

    public static final Function<Person, String> GET_NAME =
            new InstanceFunction<Person, String>("getName") {};
   
    private final boolean adult;

    public static final Predicate<Person> IS_ADULT =
            new InstancePerson<Person>("isAdult") {};
}

I created these constants only within the Instance class (if possible), and only when I need them. This approach is much shorter than writing the inner-class-fiveliner for every usecase and nearly as safe for refactoring: If the types or the name of the 'manifested' function changes, the class-loading process immediatly fails. Performance should be on par with handwritten classes, since the expensive lookup happens only once.
Reply all
Reply to author
Forward
0 new messages