Functors
Functors are objects that model operations that can be performed. In their simplest form they are somewhat like function pointers: they allow a client to call an unknown method with a standard interface. Because functors are full-blown objects they can do quite a bit more than function pointers: they can record and take advantage of their own state as well as allowing references to other objects be used in the method execution. "Smart" functors can also allow the client to ask questions about what operations are available and what the functor requires for proper execution. Basic functors dont provide this functionality but are easier to create which makes them more useful.
Basic Functors
The basic functors are Interfaces with a single-method protocol that makes them easy to implement. They are about as close to having Smalltalk block simplicity as is possible with Java right now. Functors vary on two axes: what type of value they return and how many arguments they take. There are three types of Functors based on return value:
|
Type
|
Returns
|
Method Prefix |
|
Procedure
|
nothing (void)
|
execute |
|
Function
|
An Object
|
value |
|
Predicate
|
A boolean
|
isTrue | There can be any number of parameters, but currently there is support for only 0, 1, and 2 argument functors. All functors only take Objects as parameters.
|
#
|
Type Suffix
|
Method Suffix |
|
0
|
0Arg
|
() [none] |
|
1
|
1Arg
|
With(Object arg1) |
|
2
|
2Arg
|
With_with(Object arg1, Object arg2) |
Examples
The following are some declarations of the different basic functors.
public interface Procedure0Arg {
public void execute();
}
public interface Function1Arg {
public Object valueWith(Object arg1);
}
public interface Predicate2Arg{
public boolean isTrueWith_with(Object arg1, Object arg2);
}
Calling the above functors might look like this:
boolean exampleMethod (Procedure0Arg procedure, Function1Arg function, Predicate2Arg predicate) {
procedure.execute();
return predicate.isTrueWith_with("Hi There", function.valueWith(" Hi "))
}
Using a BasicFunctor
To use a BasicFunctor you can either implement the functors interface in a standard Class or use Java 1.1s anonymous classes to create functors where you need them.
Named Functor Creation
Using the standard Class implementation looks like this:
class MyProcedure implements Procedure0Arg {
public void execute() {
System.out.println("We're here");
}
}
class MyTrimFunction implements Function1Arg {
public Object valueWith(Object arg1) {
return ((String) arg1).trim();
}
}
class MyStartsWithPredicate implements Predicate2Arg {
public boolean isTrueWith_with(Object arg1, Object arg2) {
return ((String) arg1).startsWith((String) arg2);
}
}
And creating one of these functors would look like this:
Procedure0Arg myProcedure1 = new MyProcedure();
Anonymous Functor Creation
Using the anonymous class method to define and create the Functors in one statement is a bit more obscure but is also much shorter and more convenient. It looks like this:
new Procedure0Arg () {public void execute() {
System.out.println("Were here");
}};
new Function1Arg () {public Object valueWith(Object arg1) {
return ((String) arg1).trim();
}};
new Predicate2Arg () {public boolean isTrueWith_with(Object arg1, Object arg2) {
return ((String) arg1).startsWith((String) arg2);
}};
Functor recompiler
Because using anonymous classes for Functors is still a bit noisy, we have a recompiler for Java that allows you to use "Blocks" (very similar to Smalltalk blocks) as a more concise functor creation expression. A blocks syntax is:
[ FunctorArgumentList | BlockStatements ]
where a ^ in front of an argument specification indicates the return type of the "block".
Returning to the above examples, we could call the original "exampleMethod" as follows:
if (exampleMethod (
[^void | System.out.println("We're here");],
[String arg1 | return arg1.trim();],
[^boolean, String arg1, String arg2 | return arg1.startsWith(arg2)]
)
) System.out.println("Found the string!");
where each block would expand to the above anonymous functor creation statement respectively (except there is some auto casting going on).
Smart Functors
Smart functors have the same basic abilities as basic functors but have a richer protocol that allows the client of the functor to inquire about the functors capabilities and requirements. For example, a SmartFunctor can answer the question of how many arguments it requires and whether it returns a value.
Notation
|
Since functors are a combination of an object and a function (block of code), the notation for a functor should be a combination of an object and a block of code. Because the function is within the object we get the notation to the right.
The first text line describes the Class (the functionality) of the Functor and the second line describes the parent type (the protocol) of the Functor. This symbol is frequently unwieldy because it is large to begin with and enlarges significantly as text is added.
|
 |
|
The second notation to the right is also acceptable. For this notation the objectness of the functor is visible but less dominant in area. This is generally a better symbol because it is more visually distinct and scales much better. The object can be moved around along the edge of the function.
|
 |
|