Blocks in Duby
Java typically uses inner classes where you would use a block in Ruby. For example, the Ruby thread constructor is:
Thread.new([arg]*) {|args| block }
But in Java we have:
public interface Runnable {
void run();
}
...
Thread(Runnable target)
To actually use this you would create an inner class:
new Thread(new Runnable() {
void run() { ... }
}
So in Duby we'll convert blocks into inner classes. So when you say
Thread.new { puts "Hi" }
Duby will notice that the last argument to the Thread constructor is a Runnable, and create an inner class implementing Runnable. The block will always be the last argument to called method, and it doesn't have to be an interface. If the last type is a class we'll create an anonymous subclass. The generated class will use the block body to define all the abstract methods in the interface or parent class. So in this case the generated Java will be something like this:
new Thread(new Runnable() {
void run() {
System.out.println("Hi");
}
}
We should also be able to support cases where you need to implement multiple methods, for example in a GWT RPC callback:
service.foo do
def onSuccess(result)
Window.alert(result)
end
def onFailure(ex)
Window.alert(ex.message)
end
end
Yield
For consistency and compatibility with calling things from java, when you call yield it should be implemented using an interface. I don't believe Java has a suitable interface, so we'll probably need to generate an interface each time. So if you write:
def foo(x:String) yield x # (There should probably be some way to specify the return type.) end
The Java will be something like this:
public static interface StringIter {
void yield(String x);
}
public void foo(String x, StringIter iter) {
iter.yield(x);
}
Closures
Ruby blocks are closures: if you modify a variable in the block you can see that modification outside of the block. Java's inner classes don't allow you to access any mutable local variables. This gives you nice thread safety, but it also prevents all sorts of cool things you can do with Ruby's blocks. So Duby blocks should be closures. To implement this we'll actually generate two classes: a binding class containing all the captured variables, and the actual class used for the callback. Accessing to any captured variables will go through the binding. If we imagine that arrays have an each method, you could re-implement Array.length like this:
def stupid_length(x:int[])
count = 0
x.each { count += 1 }
count
end
And the generated Java code:
private class StupidLengthBinding {
int count;
}
private static class StupidLengthIter1 {
private StupidLengthBinding binding;
StupidLengthIter1(StupidLengthBinding b) {
binding = b;
}
public void yield(int unused) {
binding.count += 1;
}
}
public int stupid_length(int[] x) {
StupidLengthBinding binding = new StupidLengthBinding();
binding.count = 0;
StupidLengthIter1 iter = new StupidLengthIter1(binding);
x.each(iter);
return binding.count;
}
In this case we don't really need a separate binding class -- we could just stick count into the Iter class. But if there's multiple blocks the binding ensures they all access the same variables.






