Skip to main content

method handles == closures ??

Posted by forax on January 2, 2009 at 7:36 AM PST

John Rose
(JSR 292 spec leader)
recently push a great patch that enables support
for method handles inside the VM.
A java.dyn.MethodHandle is a safe object that stands for
a function pointer but unlike a function pointer, calling a method handle
with wrong arguments raise an exception.
The aim of method handles (with invokedynamic) is to ease
the support of dynamic languages by the Java VM.
But method handles is also a good candidate to implement
closures :)

Using method handle VM support to implement closure

Let's take an example, I want to iterate over a collection
and call a method (here sayHello) on each element.
Using BGGA syntax, it's something like that:

  public static void each(Collection<?> c, {Object => void} closure) {
      for(Object element:c) {
          closure.invoke(element);
      }
  } 
  public static void sayHello(Object message) {
      System.out.println("hello "+message);
  }
  public static void main(String[] args) {
      {Object => void} closure=#sayHello;
      List list=Arrays.asList("BGGA", "closure");
      each(list, closure);
  }

And now using a MethodHandle:

  public static void each(Collection<?> c, MethodHandle mh) {
      for(Object element:c) {
          mh.invoke(element);
      }
  }
  public static void main(String[] args) {
      MethodType methodType=MethodType.make(void.class, Object.class);
      MethodHandle mh=MethodHandles.findStatic(MethodHandleTest.class, "sayHello", methodType);
     
      List list=Arrays.asList("davinci", "closure");
      each(list, mh);
  }

To create a method handle, I first create its method type,
here void(Object) then I perform a lookup
using findStatic to find a static method named "sayHello"
that takes an Object and returns void.
And then I can call each with a list of String and my method
handle (linked to sayHello).

Compile and ...

And it doesn't compile :(


In each, I call invoke() on a MethodHandle
and this method doesn't exist at compile time.
Remember how a method handle works,
you always can call invoke with the arguments you want
and the VM will raise an exception if arguments doesn't match
parameter types.
So, the VM understands the call, but the compiler is not already modified
to allow to call invoke on a MethodHandle with any
arguments.


Two solutions here:

  1. Wait a prototype of the
    JSR on small language changes
    because the ability to invoke a MethodHandle is one of the proposed
    changes.

  2. Generate the bytecode corresponding to that call by hand
    or better using ASM.

I don't want to wait, so solution 2.
ASM generates a class Magic with trampoline methods calling invoke
on the MethodHandle taken as parameter.

  class Magic {
    public static void invoke(MethodHandle mh, Object o) {
      // ASM will provide the magic :)
      // aload_0
      // aload_1
      // invokevirtual "invoke" "java/dyn/MethodHandle" "(Ljava/lang/Object;)V"
      // return
    }
  }

  public static void each(Collection<?> c, MethodHandle mh) {
      for(Object element:c) {
          Magic.invoke(mh, element);
      }
  }

And how to run it ?

Simple, take the hotspot source, the jdk source, take the
mlvm patch queue, apply the patches named "meth"
(no it's not for methadone), recompile
the VM, recompile the JDK.


If you're lazy, and you use linux just take the binary of beta 42,
and download this zip file


It contains two zips and a jar.

  • vms.zip contains two pre-compiled VMs: davinci-client
    and davinci-server that must be unziped in
    jdk1.7.0/jre/lib/i386/.

  • method-handles.jar contains additional classes like
    java.dyn.MethodHandle, etc.

  • src.zip contains all the sources: the example above,
    the ASM code and the additional classes all under GPL + classpath
    exception.



To run:

  java -davinci-client -Xbootclasspath/p:method-handles.jar -XX:+MethodHandles fr.umlv.davinci.test.MethodHandleTest

Some open questions: Are method handles more efficient than reflection ?
I think the answer is Yes but I let you that as an exercice.
BGGA spec allows co/contravariance how to do that with method handles ?
Hum, good question for a future blog entry.

Happy new year,


Rémi

Related Topics >>

Comments

http://mail.openjdk.java.net/pipermail/closures-dev/2008-December/000233... Seriously is it only me thinking this is a great idea?

No alternative to real closures i guess. Their great advantage (and great disadvantage) is that they capture the method signature perfectly, unfortunately including exceptions. That guy in the closures mailing list proposing to linearize exception handling has a nice idea since that would mean no throws exception E garbage in closures accepting methods, but that requires that closures do not escape the method boundaries, so no closure variables, which IMO only makes sense. I think that closures are a FP concept, and so, should not be able to be saved in variables. If that does not mean that we can't refer to a method as a closure. MyObject#method() or whatever makes perfect sense.

@liquid, > "add metraprogrammatically some method handles" As far as i know, Interface injection is still a goal of JSR292, see http://openjdk.java.net/projects/mlvm/subprojects.html#InterfaceInjection It's a nice way to get a kind of extension methods without having one of its major drawback: extension method a la C# doesn't allow virtual call. Rémi

this is seriously nice, the fact that we're both french and share almost the same first name (mais avec un y) just makes it better :) All joking aside, this a seriously cool way to get some closure-like functionnality in java 7. Maybe if we could do add metraprogrammatically some method handles to a Class object, we could have closures, extension methods, and maybe some more type inference (generics + methods handles ?) and we'd be good to go ;) This *is* letting java be java, you don't have to use it if you don't want to, it's IMHO a nice alternative to modifying the language and making everyone use one of the proposal's syntax/semantics they might not like. That looks like an interesting compromise, i'd love to hear more to make my mind about it. Merci, Rémy

@thedarksavant, Currently when i want a closure in Java, i create an anonymous class and I throw an exception when i want to escape earlier. I think it could be a little bit more user friendly. Java is a language, like any other ones, it evolves. BTW, the syntax is not the point here. The point is the runtime support. @ronaldtm, (funny alias), yes, that why i have mentioned MethodHandles.insertArgument() in my answer to vierio. Rémi

http://martinfowler.com/bliki/Closure.html

I'm no language expert, but closures' definition didn't have that 'lexical scope variable binding' thing? This is what makes closures substantially more powerful than function pointers. For example, without it, you couldn't do something like (Groovy syntax) StringBuffer buffer = ...; list.each { s -> buffer.append(s) } // references a local variable 'buffer' from the block that contains the closure , which is a pretty common idiom in languages that support closures.

@forax D'oh! So you can bind objets to methods? Now, *that* is interesting!!

Or we could let Java be Java and when we want to use a dynamic language we use a dynamic language. I've been writing and reading Java for over 10 years now, and I have to admit that reading the code above gives me a slight headache and bit of nausea. Here's how we do it in Groovy: messages.each { println "hello ${it}" } Of course Groovy already has closures. And Java-like syntax. And runs on the VM. Chasing C# is just pushing more and more Java developers to Ruby and Groovy.

@veiro, yes but no :) I agree with you if method handles was only C function pointers but you can bound an object to method handle which is the definition of a closure. By example: class Sum { int value; public void sum(Object message) { value+=(Integer)message; } } ... public static void main(String[] args) { MethodHandle sum=MethodHandles.findVirtual(Sum.class, "sum", MethodType.make(void.class, Object.class)); Sum sumObject = new Sum(); // bound sumObject to create a new method handle sum=MethodHandles.insertArgument(sum, sumObject); each(Arrays.asList(1,3), sum); System.out.println(sumObject.value); } This code is also included in the src.zip. Cheers, Rémi

There's a *huge* difference between method handles (aka C function pointers) and closures. So, of course, method handle != closure. Saying that method handles equal closures is just confusing people.

MethodHandles are much more complex than C-function pointers

A MethodHandle is definitely not as simple as a C function pointer. It is a far more complex construct than that! What Remi is saying is that the MethodHandle can (and should) be used to implement closures, or to be more precise, hold on to closures. The MethodHandle by itself does not give you lexical scoping and all the other goodness that you need.

Remi asked: BGGA spec allows co/contravariance how to do that with method handles ? My answer: You need generic invocation! :-)

Vieiro, if you want to know more about the versatility of the MethodHandles, please read http://blogs.oracle.com/ohrstrom/2009/08/using_methodhandles_to_reconci....