I've been kind of stumped the last week or so coming up with an efficient design for closures. First the solution must be fast - because we are using closures for all collection iteration which is something done all the freaking time. Second I want a closure with n parameters to be usable where a method with n or more parameters are required. You can kind of think this as default parameters in reverse - you can ignore parameters at the end if you choose.
Really what a closure "implements" is Method.call(Obj[] args):
Method m = {|Int a, Int a -> Int| return a + b}
m.call( [ 2, 3] ) // yields 5
m.call( [ 2, 3, 4] ) // this is ok too - extra args ignored
The fact that a closure implements call(Obj[]) is required - that's the way we use reflection. However using call(Obj[]) all the time would suck ass because we would be allocating Lists for every invocation. So what we really need is a call design which let's us pass the arguments on the stack just like any other method invocation. This is tricky because Method.call() by it's definition takes a variable number of parameters.
So the design I propose is called "indirect calls" because you make the call indirectly thru a Method instance (kind of like reflection). To simplify matters, we can only use indirect calls with 10 or fewer parameters. Any more than 10 parameters is pretty poor design, but if it must be done - you can always use call(Obj[]). With a parameter cap, we can map this into eleven specialized methods on Method: call0, call1, ... call10. Now the call site can use a normal stack based virtual method call.
For normal Methods these methods will actually route to call(Obj[]) since we are using reflection. However for curried methods and closures, we can override these methods for efficient implementation. So from our example above, the closure would compile to something like this:
Note how calls with fewer than 2 parameters are considered errors, and more than 2 parameters just route to call2() which provides the actual implementation (including the generic call(Obj[]) version). For compression, I can refactor this into 11 reusable Method subclasses:
abstract class Method2 extends Method
{
Obj call(Obj[] args) { return call2(args[0], args[1]) }
Obj call0() { throw Err.make }
Obj call1() { throw Err.make }
abstract Obj call2(Obj a, Obj b)
Obj call3(Obj a, Obj b, Obj c) { return call2(a, b); }
Obj call4(Obj a, Obj b, Obj c, Obj d) { return call2(a, b); }
...
}
class ClosureX extends Method2
{
Obj call2(Obj a, Obj b) { return (Int)a + (Int)b; }
}
So this is how curried methods and closures will actually be implemented - as subclasses of one of these 11 abstract Method classes. The constructor of the closure will take whatever variables are closed over. So with this design a typical closure iteration has the same overhead as using a Iterator in Java or C#. We have a heap allocation for the closure itself. Then the calls are just normal virtual method calls, with potentially at most one redirect call if the parameters counts are different (which should be optimized as a hotspot since they will be captured in one base class).
For callers, we will provide a bit of syntactic sugar if using less than 10 arguments:
if m is a Method:
m(2, 3) == m.call2(2, 3) == m.call([2, 3])
brian Sun 22 Jan 2006
I've been kind of stumped the last week or so coming up with an efficient design for closures. First the solution must be fast - because we are using closures for all collection iteration which is something done all the freaking time. Second I want a closure with n parameters to be usable where a method with n or more parameters are required. You can kind of think this as default parameters in reverse - you can ignore parameters at the end if you choose.
Really what a closure "implements" is Method.call(Obj[] args):
The fact that a closure implements call(Obj[]) is required - that's the way we use reflection. However using call(Obj[]) all the time would suck ass because we would be allocating Lists for every invocation. So what we really need is a call design which let's us pass the arguments on the stack just like any other method invocation. This is tricky because Method.call() by it's definition takes a variable number of parameters.
So the design I propose is called "indirect calls" because you make the call indirectly thru a Method instance (kind of like reflection). To simplify matters, we can only use indirect calls with 10 or fewer parameters. Any more than 10 parameters is pretty poor design, but if it must be done - you can always use call(Obj[]). With a parameter cap, we can map this into eleven specialized methods on Method: call0, call1, ... call10. Now the call site can use a normal stack based virtual method call.
For normal Methods these methods will actually route to call(Obj[]) since we are using reflection. However for curried methods and closures, we can override these methods for efficient implementation. So from our example above, the closure would compile to something like this:
Note how calls with fewer than 2 parameters are considered errors, and more than 2 parameters just route to call2() which provides the actual implementation (including the generic call(Obj[]) version). For compression, I can refactor this into 11 reusable Method subclasses:
So this is how curried methods and closures will actually be implemented - as subclasses of one of these 11 abstract Method classes. The constructor of the closure will take whatever variables are closed over. So with this design a typical closure iteration has the same overhead as using a Iterator in Java or C#. We have a heap allocation for the closure itself. Then the calls are just normal virtual method calls, with potentially at most one redirect call if the parameters counts are different (which should be optimized as a hotspot since they will be captured in one base class).
For callers, we will provide a bit of syntactic sugar if using less than 10 arguments: