Closures: Control Abstraction, Method References, Puzzler Solution
Closures: Control Abstraction, Method References, Puzzler Solution
The Java Closures prototype now supports control abstraction and implements restricted closures and function types. The syntax has changed slightly. Also, as hinted in the draft JSR proposal, there is now support for eta abstraction, which is called method reference in Stephen Colebourne's FCM proposal. We haven't updated the specification, so this will serve as a brief tutorial on the changes until we do. I don't know if this will be the syntax we will end up with, but it will do for now. Finally, we look at solutions to the closure puzzler in my previous post.
Control Abstraction
The first thing you'll notice when using the new prototype is that the compiler gives a warning when a closure uses a local variable from an enclosing scope:
Example.java:4: warning: [shared] captured variable i not annotated @Shared Runnable r = { => System.out.println(i); }; ^
There are a few ways to make this warning go away:
- declare the variable
final
; or - annotate the variable
@Shared
; or - make sure the variable is not the target of any assignment expression; or
- put
@SuppressWarnings("shared")
on an enclosing method or class; or - use an unrestricted closure, by using the
==>
token instead of the=>
token (when possible).
The =>
token builds a restricted closure that
triggers this warning. Restricted closures also do not allow a
break
or continue
statement to a target outside
the closure, nor a return
statement from the enclosing method.
You will rarely want to write an unrestricted closure; many (but not all) of
the things you need to do with an unrestricted closure can be expressed more
clearly with a control invocation statement instead.
You're not allowed to assign an unrestricted closure to a restricted
interface. A number of existing JDK interfaces, such as
java.lang.Runnable
, have been modified to be restricted.
Error: cannot assign an unrestricted closure to a restricted interface type
Runnable r = { ==> System.out.println(i); };
^
In the less common case that you're writing a method intended to be used as a control
API, you can write a function type with the (new) ==>
token to designate
an unrestricted function (interface) type. Let's do that to write a method,
with
, that will automatically close a stream for us. The idea is to be able
to replace this code
FileInputStream input = new FileInputStream(fileName);
try {
// use input
} finally {
try {
input.close();
} catch (IOException ex) { logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
with this
with (FileInputStream input : new FileInputStream(fileName)) { // use input }
which is an invocation of the following method
public static void with(FileInputStream t, {FileInputStream==>void} block) {
try {
block.invoke(t);
} finally {
try {
t.close();
} catch (IOException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
This is among the simplest control APIs, but it has some limitations:
- It works with the type
FileInputStream
, but not any otherCloseable
types - It does not support exception transparency
- It does not support completion transparency
Completing the API by repairing these defects is left as an exercise to the reader. A solution will be discussed in my JavaOne talk Closures Cookbook.
Method References
A natural companion to closures is a way to refer to an existing method instead of writing a closure that accepts the same arguments and just invokes the method. This is sometimes known as eta abstraction or method references. We expect closures in their final form to include support for this convenient feature, which is why it is called out in the draft JSR proposal. The latest version of the prototype supports this, with a syntax based on javadoc conventions. Here are a few examples:
{ int => Integer } integerValue = Integer#valueOf(int); { Integer => String } integerString = Integer#toString(); { int, int => int } min = Math#min(int, int); { String => void } println = System.out#println(String); { => String } three = new Integer(3)#toString(); { Collection<String> => String } max = Collections#max(Collection<String>); { => Collection<String> } makeEmpty = Collections#<String>emptySet(); Runnable printEmptyLine = System.out#println();
Writing code as a method is sometimes more convenient than writing it as a closure:
void doTask() { // a complex task to be done in the background } Executor ex = ...; ex.execute(this#doTask());
Puzzler Solution
A couple of weeks ago we looked at a Java puzzler involving closures, and a number of people discussed the underlying issue. My favorite is David's post "Color-flavor locking breaks chiral symmetry". Lessons include not exposing public fields (accessors are better) and being careful to avoid cyclic initialization dependencies.
The enum language feature provides support for one solution to the puzzle: specialize each instance of the enums.
import java.util.*; enum Color { BROWN { public Flavor flavor() { return Flavor.CHOCOLATE; } }, RED { public Flavor flavor() { return Flavor.STRAWBERRY; } }, WHITE { public Flavor flavor() { return Flavor.VANILLA; } }; abstract Flavor flavor(); } enum Flavor { CHOCOLATE { public Color color() { return Color.BROWN; } }, STRAWBERRY { public Color color() { return Color.RED; } }, VANILLA { public Color color() { return Color.WHITE; } }; abstract Color color(); } class Neapolitan { static <T,U> List<U> map(List<T> list, {T=>U} transform) { List<U> result = new ArrayList<U>(list.size()); for (T t : list) { result.add(transform.invoke(t)); } return result; } public static void main(String[] args) { List<Color> colors = map(Arrays.asList(Flavor.values()), { Flavor f => f.color() }); System.out.println(colors.equals(Arrays.asList(Color.values()))); List<Flavor> flavors = map(Arrays.asList(Color.values()), { Color c => c.flavor() }); System.out.println(flavors.equals(Arrays.asList(Flavor.values()))); } }
Another elegant solution, due to 5er_levart, uses closures:
enum Color { BROWN({=>Flavor.CHOCOLATE}), RED({=>Flavor.STRAWBERRY}), WHITE({=>Flavor.VANILLA}); private final {=>Flavor} flavor; public Flavor flavor() { return flavor.invoke(); } Color({=>Flavor} flavor) { this.flavor = flavor; } } enum Flavor { CHOCOLATE({=>Color.BROWN}), STRAWBERRY({=>Color.RED}), VANILLA({=>Color.WHITE}); private final {=>Color} color; public Color color() { return color.invoke(); } Flavor({=>Color} color) { this.color = color; } }
In both solutions the idea is to compute the value lazily, a key technique to break dependency cycles.