I've introduced two changes to the syntax to support functional programming: the () call operator and the & curry operator.
The first change is that any variable typed as a Method, may be invoked using the () operator. Previously you had to use one of the Method.call methods to invoke the method which was a bit awkward:
// previously
m := |Int a, Int b->Int| { return a + b }
m.call2(5, 6)
// new shortcut
m(5, 6)
Note a couple of obscure boundary conditions: if you are chaining the () operator on a method which itself returns a Method, then you might have to specify an empty () to ensure the right binding. If you are calling a field which has a Method, then you have to use parenthesis to achieve the right binding. Also I removed a "feature" where local variables didn't hide calling methods on your enclosing type - since local variables can now be invoked directly, I decided to remove this ambiguity (which I found I was using in about 30 places in my compiler code).
The second change is full support for functional currying using the new & curry operator. Currying is the partial evaluation of a method, where zero or more arguments are specified, and a new method is returned which takes the unspecified parameters. Consider the simplest case:
class Foo
{
static Int add(Int a, Int b) { return a + b }
static Void main() { m := &add; echo(m(3, 4)) }
}
In this simplest case we have a static method, and zero arguments are specified. This form of currying is basically a shortcut for reflecting Foo's add method. This will actually be a handy feature because only static methods will be allowed as the run method to a Thread. Now lets look at a slightly more complicated example:
class Foo
{
static Int add(Int a, Int b) { return a + b }
static Void main() { m := &add(3); echo(m(4)) }
}
In this case we curried the add method which took two parameters, into a new method which takes one parameter to be added with 3. Behind the scenes this is implemented by the compiler similar to closures - a synthetic class is generated which subclasses Method to store the captured parameters and redirect to the original method when invoked.
Where things get really interesting is when we curry OO instance methods - which is definitely something you want to do when setting up callbacks. If you curry an instance method with no target then you get a new method where the first parameter is the "this" target:
m := &Int.plus
echo(m(6, 2))
In this case we curried an instance method that requires an Int instance, plus one other Int parameter, so m is typed |Int a, Int b->Int|. But we can curry Int.plus using a prefined "this" to create a method which adds 2 to a given Int parameter:
plusTwo := &2.plus
echo(plusTwo(3))
Probably a more common scenario is you curry instance methods using implicit this to setup callbacks, and potentially predefined callback arguments:
We talked about this exact design over a year ago, and I decided to go ahead an finish it up. I've already run into a couple places where a curried method is more elegant than a closure.
brian Mon 22 Jan 2007
I've introduced two changes to the syntax to support functional programming: the () call operator and the & curry operator.
The first change is that any variable typed as a Method, may be invoked using the () operator. Previously you had to use one of the Method.call methods to invoke the method which was a bit awkward:
Note a couple of obscure boundary conditions: if you are chaining the () operator on a method which itself returns a Method, then you might have to specify an empty () to ensure the right binding. If you are calling a field which has a Method, then you have to use parenthesis to achieve the right binding. Also I removed a "feature" where local variables didn't hide calling methods on your enclosing type - since local variables can now be invoked directly, I decided to remove this ambiguity (which I found I was using in about 30 places in my compiler code).
The second change is full support for functional currying using the new & curry operator. Currying is the partial evaluation of a method, where zero or more arguments are specified, and a new method is returned which takes the unspecified parameters. Consider the simplest case:
In this simplest case we have a static method, and zero arguments are specified. This form of currying is basically a shortcut for reflecting Foo's add method. This will actually be a handy feature because only static methods will be allowed as the run method to a Thread. Now lets look at a slightly more complicated example:
In this case we curried the add method which took two parameters, into a new method which takes one parameter to be added with 3. Behind the scenes this is implemented by the compiler similar to closures - a synthetic class is generated which subclasses Method to store the captured parameters and redirect to the original method when invoked.
Where things get really interesting is when we curry OO instance methods - which is definitely something you want to do when setting up callbacks. If you curry an instance method with no target then you get a new method where the first parameter is the "this" target:
In this case we curried an instance method that requires an Int instance, plus one other Int parameter, so m is typed |Int a, Int b->Int|. But we can curry Int.plus using a prefined "this" to create a method which adds 2 to a given Int parameter:
Probably a more common scenario is you curry instance methods using implicit this to setup callbacks, and potentially predefined callback arguments:
We talked about this exact design over a year ago, and I decided to go ahead an finish it up. I've already run into a couple places where a curried method is more elegant than a closure.