External reference: FCM v0.5

Stephen Colebourne scolebourne at joda.org
Tue Mar 2 05:44:11 PST 2010


Following Neal's lead, here is the text of the FCM proposal v0.5 as
formal submission for Lambda.
http://docs.google.com/View?id=ddhp95vd_6hg3qhc

The semantic and syntax choices sections at the end I believe continue
to be pertinent for this discussion, as they document /why/ we chose
what we chose.

The following were changed in the unpublished v0.6 of FCM
-  Removed named inner methods
- Only allow conversion to single method interfaces, not single
abstract method classes
- Clarify types of method, constructor and field literals
- Propose generic parameters for Method and Field
- Clarify conversion of references when syntax is ambiguous
- Add arrow rule for conversion
- Note transparency limitations when interacting with single method interfaces
If there is interest, I'll tart up and publish FCM v0.6.

Stephen

--------------------------------------
 First-class methods: Java-style closures

Version 0.5

Stephen Colebourne, UK
Stefan Schulz, Germany

I. Problem

In Java, the method is the principal construct where application logic
resides. At present, a method can only exist within a class and cannot
be manipulated or referenced in any simple manner. Other languages
permit a block of application logic to be referenced outside of a
class, and this is often referred to as a function or a closure.


A Java developer can approach the power of functions/closures using
two techniques - reflection [5] and inner classes [1:15.9]. However,
neither is very developer friendly - reflection is not compile-time
safe or refactorable, and inner classes are very verbose.


As a result of the difficulty of writing code using reflection and
inner classes the techniques tend to be limited to callbacks between
an application and a framework. However, the majority of uses of
functions or closures in other languages is for small-scale code
re-use. This typically involves refactoring out common sections of
code which may have a common beginning and end, but different code in
the middle. In this scenario, the extracted code isn't run at a much
later time like a callback is, but immediately as the original code
would have done.


To enable this power in Java, we propose to change the language to
support the referencing and definition of methods as first-class
objects. This provides much of the power of functions/closures but in
a style familiar to Java developers.


We further propose to enable the referencing of other class members -
constructors and fields - as first-class citizens. This is a natural
extension to First-class Methods.


Sorting example

The Comparator interface is the standard mechanism in Java to provide
the code needed to sort lists. Here is a Comparator that sorts by
length of String:

  List<String> list = ...
  Collections.sort(list, new Comparator<String>() {
    public int compare(String str1, String str2) {
      return str1.length() - str2.length();
    }
  });

With the changes in this proposal, the code could be written as follows:

  List<String> list = ...
  Collections.sort(list, #(String str1, String str2) {
    return str1.length() - str2.length();
  });

This syntax is termed an anonymous inner method, and is a logical
extension to the anonymous inner class concept. The conversion to a
Comparator is automatic.

Event listener example

The Swing GUI framework makes widespread use of listeners to inform
the application of events. These are frequently implemented as inner
classes which delegate their behaviour to a 'normal' method on the
class initialising the listener:

  public void init() {
    JButton button = ...;
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        handleAction(ev);
      }
    });
  }
  public void handleAction(ActionEvent ev) {
    // handle event
  }

With the changes in this proposal, the code could be written as follows:

  public void init() {
    JButton button = ...;
    button.addActionListener(this#handleAction(ActionEvent));
  }
  public void handleAction(ActionEvent ev) {
    // handle event
  }

This syntax uses a bound method reference to refer to a pre-existing
method on a specific object. The conversion to an ActionListener is
automatic.

II. Member literals

Member literals represent a literal form, similar to double quoted
Strings, for referring to a class's members. There are three kinds of
literals each referring to the respective type of member: method
literals (1), constructor literals (2), and field literals (3).

MemberLiteral:

    MethodLiteral

    ConstructorLiteral

    FieldLiteral


All kinds of member literals follow the standard rules for visibility.
A member literal may refer to a public member from anywhere, to a
protected member only from subclasses and within the same package, to
a default scope member from within the same package, and to a private
member only from within that class.

1. Method literals

A method literal refers to a method definition. The syntax consists of
the class name, followed by the # (hash) symbol and the method name
with parameter types.

Rationale: The use of the # symbol is deliberate. The symbol is
already used within Javadoc to separate the class name from the method
name in exactly this manner. The method literal syntax merely brings
the Javadoc convention into Java source code.
We go on to use the same # symbol throughout the proposal for method
related syntaxes, helping to provide consistency.


Examples

The following examples refer to static methods:

  Math#min(int, int)
  Collections#sort(List, Comparator)

The following examples refer to instance methods:

  Integer#intValue()
  Object#equals(Object)

Detail

Method literals are, by definition, assignable to
java.lang.reflect.Method. As such, it is possible to call any part of
the API of Method directly:

  Class<?> returnType = Integer#intValue().getReturnType();

This calls the getReturnType method on the Method object contextually
created by the method literal.


Syntax

MethodLiteral:

    ClassType # Identifier ( TypeListopt )
2. Constructor literals
A constructor literal refers to a constructor definition. The syntax
consists of the class name, followed by the # (hash) symbol and the
constructor's parameter types.

Examples

  ArrayList#(int)
  JButton#(String, Icon)

Detail

Constructor literals are by definition assignable to
java.lang.reflect.Constructor<T>, where T must be assignable from the
referred class.

It is a compile-time error to reference an abstract class' constructor.

Syntax

ConstructorLiteral:
    ClassType # ( TypeListopt )
3. Field literals
A field literal refers to a field definition. The syntax consists of
the class name, followed by the # (hash) symbol and the field's name.

Examples

The following examples refer to static fields:

  Member#DECLARED
  Node#ELEMENT_NODE

The following examples refer to instance fields:

  MyBean#name
  TaskEntry#priority

Detail

Field literals are by definition assignable to java.lang.reflect.Field.

Syntax

FieldLiteral:
    ClassType # Identifier

III. Method types
Method types are a way to define the signature of a method or block of
code. As such, they must define the parameters, return type, and
thrown exceptions.

The syntax consists of the # (hash) symbol followed by parentheses.
Within the parentheses the method signature is specified following the
logical order of defining a method - the return type, followed by the
parameter types in parentheses, followed by any exceptions.

Rationale: The use of the # symbol provides a clear visual indication
to the developer as to the start of the method type. We feel that
using # is considerably clearer in use than the alternatives we
examined, including using braces (curly brackets) and a Haskell like
notation [2].
The order and style of the method type is very deliberately following
that of a method declaration in Java. This is to allow developers to
pickup the new syntax easily.

Examples

The following examples show various forms of method types, with
various return types, parameters, and thrown exceptions:

  #(int(String, String))

  #(void(String))

  #(String(File) throws IOException)

  #(long())

The following example shows how a method type can be assigned to an
ActionListener interface:

  #(void(ActionEvent)) method = ...
  ActionListener lnr = method;


This will compile to code equivalent to:

  static class Outer$MethodType$1 implements ActionListener {
    private final #(void(ActionEvent)) target;
    public Outer$MethodType$1(#(void(ActionEvent)) target) {
      this.target = target;
    }
    public void actionPerformed(ActionEvent arg1) {
      target.invoke(arg1);
    }
  };
  #(void(ActionEvent)) method = ...
  ActionListener im = new Outer$MethodType$1(method);

Detail

An instance of a method type supports a single method, invoke, that
can be used to call the method. Both the arguments and the return type
of the invoke method are fully type-safe. For example:

  #(boolean(String,String)) nullSafeEqualsMethod = ...
  boolean equal = nullSafeEqualsMethod.invoke("Hello", "Hi");


Method types will be implemented as a compiler-generated interface.
Considerable investigation and study of the implementation of this
concept has already been undertaken as part of the BGGA proposal [4]
(function types). As such, we don't attempt to duplicate that work
here.

A method type may be assigned to a class or interface provided that
the compiler-generated class has a single abstract method with a fully
compatible signature. It is a compile error if there is zero or more
than one abstract method.

Syntax

Type:

    MethodType


MethodType:

    # ( ResultType ( TypeListopt ) Throwsopt )

IV. Method references
Method references are a way to refer to a specific method and its
environment such that it can be invoked. There are four kinds of
method references: static method reference (A1), instance method
references (A2), bound method references (A3) and constructor
references (A4). They share a common auto-conversion mechanism for
assigning a method reference to a class or interface (B).

MethodReference:

    StaticMethodReference

    InstanceMethodReference

    BoundMethodReference

    ConstructorReference
A1. Static method references
A static method reference provides a type-safe way to obtain an object
that can be used to invoke a static method.

The syntax consists of the class name, followed by the # (hash) symbol
and the static method name with parameter types.

Examples

The following example shows the method type to which a static method
reference can be assigned:

  #(int(int, int)) ref = Math#min(int, int);

This will compile to code equivalent to:

  static class Outer$StaticMethodReference$1 implements #(int(int, int)) {
    public int invoke(int arg1, int arg2) {
      return Math.min(arg1, arg2);
    }
  };
  #(int(int, int)) ref = new Outer$StaticMethodReference$1();

Detail

A static method reference has the same syntax form as a method
literal. It becomes a method reference when it is assigned to, or
passed to, a method type.

At compilation time, the static method reference will be implemented
by a compiler-generated class. The class will implement the method
type that the reference is assigned to.

Syntax

StaticMethodReference:

    ClassType # Identifier ( TypeListopt )
A2. Instance method references
An instance method reference provides a type-safe way to obtain an
object that can be used to invoke an instance method. An instance
method must be invoked on an object, so the reference is adjusted to
pass the object as the first parameter.

The syntax consists of the class name, followed by the # (hash) symbol
and the instance method name with parameter types.

Examples

The following example shows the method type to which an instance
method reference can be assigned. Note how the method type takes an
additional parameter which is the object that will be invoked:

  #(int(List, Object)) ref = List#indexOf(Object);

This will compile to code equivalent to:

  static class Outer$InstanceMethodReference$1 implements #(int(List, Object)) {
    public int invoke(List arg1, Object arg2) {
      return arg1.indexOf(arg2);
    }
  };
  #(int(List, Object)) ref = new Outer$InstanceMethodReference$1();

Detail

An instance method reference provides a bridge between object-oriented
programming and procedural programming. It is the equivalent of
turning an instance method into a static method, by passing the
instance to be invoked as the first argument.

An instance method reference has the same syntax form as a method
literal. It becomes a method reference when it is assigned to, or
passed to, a method type.

At compilation time, the instance method reference will be implemented
by a compiler-generated class. The class will implement the method
type that the reference is assigned to.

Syntax

InstanceMethodReference:

    ClassType # Identifier ( TypeListopt )
A3. Bound method references
A bound method reference provides a type-safe way to obtain an object
that can be used to invoke an instance method. To achieve this, it
captures the object that it is to be invoked on.

The syntax consists of an expression yielding an object, followed by
the # (hash) symbol and the method name with parameter types.

Examples

The following example shows the method type to which a static method
reference can be assigned:

  List<String> list = ...
  #(int(Object)) ref = list#indexOf(Object);

This will compile to code equivalent to:

  static class Outer$BoundMethodReference$1<T> implements #(int(Object)) {
    private final List<T> target;
    public Outer$BoundMethodReference$1(List<T> target) {
      this.target = target;
    }
    public int invoke(Object arg1) {
      return target.indexOf(arg1);
    }
  };
  List<String> list = ...
  #(int(Object)) ref = new Outer$BoundMethodReference$1<String>(target);

Detail

A bound method reference is more complex than a static or instance
method reference because it captures the object that it is invoked on.
This capturing provides a very quick and easy way to pass a callback
to a framework.

At compilation time, the bound method reference will be implemented by
a compiler-generated class which stores the target object as an
instance variable. The class will implement the method type that the
reference is assigned to.

Syntax

BoundMethodReference:

    Primary # Identifier ( TypeListopt )
A4. Constructor references
A constructor reference provides a type-safe way to obtain an object
that can be used to invoke a constructor.

The syntax consists of the class name, followed by the # (hash) symbol
and the parameter types.

Examples

The following example shows the method type to which a constructor
reference can be assigned:

  #(Integer(int)) ref = Integer#(int);

This will compile to code equivalent to:

  static class Outer$ConstructorReference$1 implements #(Integer(int)) {
    public Integer invoke(int arg1) {
      return new Integer(arg1);
    }
  };
  #(Integer(int)) ref = new Outer$ConstructorReference$1();

Detail

A constructor reference has the same syntax form as a constructor
literal. It becomes a constructor reference when it is assigned to, or
passed to, a method type.

At compilation time, the constructor reference will be implemented by
a compiler-generated class. The class will implement the method type
that the reference is assigned to.

Syntax

ConstructorReference:

    ClassType # ( TypeListopt )

B. Common rules and mechanisms

Conversion to class or interface

A method reference of any of the four kinds may be assigned to a class
or interface providing that the compiler-generated class has a single
abstract method with a fully compatible signature. It is a compile
error if there is zero or more than one abstract method.

The following example on a bound method reference creates a
compiler-generated class implementing ActionListener where the single
abstract method actionPerformed calls the handleAction method on this.

  ActionListener lnr = this#handleAction(ActionEvent ev);

This assignment will be compiled to code equivalent to:

  static class Outer$InstanceReference$1 implements ActionListener {
    private final Outer target;
    public Outer$BoundMethodReference$1(Outer target) {
      this.target = target;
    }
    public void actionPerformed(ActionEvent arg1) {
      return target.handleAction(arg1);
    }
  };
  ActionListener lnr = new Outer$InstanceReference$1(this);

If the method handleAction is not accessible for any reason, a
synthetic method will be generated by the compiler.

The example on a constructor reference below creates a
compiler-generated class implementing ViewFactory where the single
abstract method create invokes the constructor on IconView.

  ViewFactory vf = IconView#(Element);

This assignment will be compiled to code equivalent to:

  static class Outer$ConstructorReference$1 implements ViewFactory {
    public View create(Element arg1) {
      return new IconView(arg1);
    }
  };
  ViewFactory vf = new Outer$ConstructorReference$1();

V. Inner methods
Inner methods are a way of writing a method in-line within the body of
another method. This is analogous to an inner class but for methods.
There are two kinds of inner method: anonymous (A1) and named (A2).
Both of which share some common rules and mechanisms (B).

InnerMethod:

    AnonymousInnerMethod

    NamedInnerMethod
A1. Anonymous inner methods
An anonymous inner method is a method written within the body of
another method. It has no name and may be assigned to a method type or
a class/interface with a single abstract method.

Examples

The following example creates an anonymous inner method assigned to a
method type:

  #(void(ActionEvent)) action = #(ActionEvent ev) {
    System.out.println("ActionEvent fired");
  }

This will compile to code equivalent to:

  static class Outer$InnerMethod$1 implements #(void(ActionEvent)) {
    public void invoke(ActionEvent arg1) {
      System.out.println("ActionEvent fired");
    }
  };
  #(void(ActionEvent)) im = new Outer$InnerMethod$1();


The following related example creates an anonymous inner method
assigned to a single abstract method interface:

  ActionListener action = #(ActionEvent ev) {
    System.out.println("ActionEvent fired");
  }

This will compile to code equivalent to:

  static class Outer$InnerMethod$1 implements ActionListener {
    public void actionPerformed(ActionEvent arg1) {
      System.out.println("ActionEvent fired");
    }
  };
  ActionListener im = new Outer$InnerMethod$1();

Detail

An anonymous inner method may be assigned to a matching method type or
a single abstract method type.

At compilation time, the anonymous inner method will be implemented as
a method on a compiler-generated class. The class generated depends on
the context to which the inner method is assigned:

    * If the context is a method type, then the compiler-generated
class implements the method type and provides a type-safe invoke
method containing the code from the inner method.

    * If the context is a class or interface with a single abstract
method, then the compiler-generated class extends or implements the
type and provides an implementation for the abstract method containing
the code from the inner method.


It is a compiler error to declare an anonymous inner method without a
context type. It is also a compile error, if there is zero or more
than one abstract method on the context class/interface.

Syntax

AnonymousInnerMethod:
    # InnerMethodParametersopt Block

InnerMethodParameters:
    ( FormalParameterListopt )
A2. Named inner methods
A named inner method is a method written within the body of another
method that has a name associated with it.

Examples

The following example creates a named inner method assigned to a
ThreadLocal with the aim of initialising the object correctly:

  private final AtomicInteger nextId = new AtomicInteger(0);
  private final ThreadLocal<Integer> threadId = #initialValue() {
    return nextId.getAndIncrement();
  };

This will compile to code equivalent to:

  static class Outer$InnerMethod$1 extends ThreadLocal<Integer> {
    public Integer initialValue() {
      return nextId.getAndIncrement();
    }
  };
  ThreadLocal<Integer> threadId = new Outer$InnerMethod$1();

Detail

A named inner method may be assigned to a matching method type or the
majority of other types. The name is used to determine which method
will be overridden.

At compilation time, the named inner method will be implemented as a
method on a compiler-generated class. The class generated depends on
the context to which the inner method is assigned.

    * If the context is a method type, then the compiler-generated
class implements the method type and provides a type-safe invoke
method containing the code from the inner method. In this case, the
name is ignored.


    * If the context is a class or interface, then the
compiler-generated class extends or implements the type and provides
an implementation with a method containing the code from the inner
method. The name and signature of the inner method must match a method
from the context type such that the method is overridden. The
resulting class must be concrete.


Rationale: Previous versions of FCM proposed implementing abstract
methods that were not overridden with empty stubs. After
consideration, it was decided that this is too risky, and has been
removed. As a result, the 'method compound' concept from v0.4 has also
been removed. Thus you cannot implement MouseListener using FCM - for
that you need an inner class.

It is a compiler error to declare a named inner method without a context type.

Syntax

NamedInnerMethod:
    # Identifier InnerMethodParametersopt Block

B. Common rules and mechanisms
The content of inner methods has the following rules:

    * The return keyword returns from the method
    * The continue and break keywords are limited to the scope of the method
    * The this keyword refers to the nearest enclosing class in the source code
    * Private methods on the nearest enclosing class in the source
code may be accessed
    * There is no ability for the inner method to access member
methods or variables on the class that actually implements it
    * Local variables from the enclosing scope may be accessed, both
read and write


These rules are similar to those of inner class methods with two
exceptions, the this keyword and local variables.

Rationale: These rules are at the heart of what differentiates one
closure proposal from another. The CICE proposal [3] provides a means
to simplify the creation of inner classes. The complex semantics of
this remain. The BGGA proposal [4] takes the opposite route and tries
to remove all restrictions on the content of the block. This results
in a different set of issues, such as using exceptions for control
flow and the RestrictedFunction interface which considerably changes
the allowed content of the block.

The use of the keyword return to return a value from the inner method
is driven by a desire to stay close to the existing Java syntax for
methods. The BGGA proposal [4] uses the concept of last-line-no
semicolon to return a value from a closure. We consider that
error-prone, inflexible (as it doesn't allow early return) and unlike
any other part of Java syntax.

See the semantic choices section for more detail.

Accessing this

The this keyword applies to the nearest enclosing class within the
source code. (i.e., it does not apply to the compiler-generated class
that is created to hold the method when implemented.) The reason for
this is simple - as far as the developer is concerned there is no
visible class in the source code for this to refer to.

In the following (contrived) example, the log method is called using
this. The object that will be passed to the log method is the instance
of MyClass that surrounds the inner method.

  public class MyClass {
    public void process() {
      #(void(String)) example = #(String message) {
        Logger.log(this, message);
      };
      example.invoke("Logged message");
    }
  }

Accessing local variables

Local variables in the enclosing scope will be fully accessible to the
inner method. If the local variable has been declared final, then it
will be read-only. Otherwise, it will be read-write. The conversion to
allow local variables to be accessed from two places will occur at
compile-time.

The method that receives an inner method may run concurrently with the
method that created the inner method. This might cause a race
condition with any shared local variables. We propose an annotation,
@ConcurrentLocalVariableAccess, that applies to the method parameter
which receives the inner method. This can be used to act as a warning
to the compiler and development environments to help avoid race
conditions.

Rationale: These rules for accessing local variables are the least
restrictive possible. Any local variable may be accessed by any inner
method. To avoid our previous concerns with race conditions we propose
an annotation, used to provide warnings and development environment
support.

See the semantic choices section for more detail.

Syntax when no parameters

For an inner method not having parameters the parentheses may be omitted:

  #{ ... }
  #methodName{ ... }

Rationale: The no argument case is fairly common, and the syntax looks
considerably more appealing with the parentheses removed.

Transparency

When an inner method is invoked immediately (synchronously) by the
higher order method that it is passed to, it can be useful to handle
exceptions more fully. What is required, is that any checked exception
that could be thrown by a statement in the inner method block is
transparently made to be an exception of the higher order method.

The BGGA proposal [4] provides mechanisms to achieve this, and we
reference that work here.

VI. Further examples
The following sections present some application scenarios for the
proposed language enhancements.

Executor

The executor part of the concurrency framework allows a task to be
submitted to run in another thread. This is typically written as an
inner class at present:

  Executor exec = ...;
  exec.execute(new Runnable() {
    public void run() {
      // code to run in another thread
    }
  });

With the changes in this proposal, the code could be written as an
anonymous inner method as follows:

  Executor exec = ...;
  exec.execute(#{
    // code to run in another thread
  });

ThreadLocal initialization

The ThreadLocal class provides a protected method to allow easy
initialization. This is intended to be used as an inner class at
present:

  private static final AtomicInteger nextId = new AtomicInteger(0);
  private static final ThreadLocal<Integer> threadId = new
ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
      return nextId.getAndIncrement();
    }
  };

With the changes in this proposal, the code could be written as a
named inner method as follows:

  private static final AtomicInteger nextId = new AtomicInteger(0);
  private static final ThreadLocal<Integer> threadId = #initialValue() {
    return nextId.getAndIncrement();
  };

Inner loop example

Java's collection framework provides the mechanism of iterators to
loop on a collection's entries outside the collection. A common code
pattern to find a matching entry looks like follows:

  PersonCache personCache = ...
  Person found = null;
  for (Person person : personCache.getPersonList()) {
    if (person.getAge() >= 18) {
      found = person;
      break;
    }
  }

With the changes in this proposal, a method could be provided directly
by the domain model, e.g., PersonCache allowing the code to be written
as follows:

  PersonCache personCache = ...
  Person found = personCache.find(#(Person person) {
    return person.getAge() >= 18;
  });

This syntax uses an anonymous inner method to return a boolean true or
false value. The inner method is called for each Person until the
match is found and the inner method returns true.

VII. Open issues

   1. Is a widening conversion reasonable, either in general or on SAM
types, e.g. for eliding unused parameters
   2. API to handle method types
   3. Is there a shortcut syntax to avoid specifying the types in a
method literal or invocable method reference when the method is not
overloaded, for example Object#equals(...)
   4. Field references (any discussion on property references are out
of scope for this document)
   5. Is there a way to add support for currying and recursiveness
from inner methods


Semantic choices
This section is Rationale about our semantic choices.

In discussing this proposal we identified three basic use-cases -
asynchronous (callbacks, like swing event listeners), synchronous
(inline code refactoring, like sorting) and control abstraction
(keyword based, like withLock or usingFile). The way in which each
'closure' proposal handles these cases is key to understanding their
differences.

With FCM we have, after much consideration, decided that the
asynchronous and synchronous use cases do not truly differ in
semantics. The control abstraction use case differs significantly
however.

With both the asynchronous and synchronous use case, an application
developer can create an instance via an inner method and assign it to
a local variable. The normal case is that the local variable is then
passed to a method as a parameter (either an asynchronous or
synchronous use case method). The local variable holding the inner
method then falls out of scope at the end of the method it is defined
in:

  public Person findPerson(PersonCache cache) {
    #(boolean(Person)) matcher = #(Person person) {...};
    return cache.find(matcher);
  }

In the example above, the matcher is being used synchronously - the
matcher is created and used inline within a single method. However, it
is possible to refactor the code slightly:

  public Person findPerson(PersonCache cache) {
    #(boolean(Person)) matcher = createMatcher();
    return cache.find(matcher);
  }
  public #(boolean(Person)) createMatcher() {
    return #(Person person) {...};
  }

Now, the matcher is being created in one method, and used in another.
Is this still a synchronous use case? Or an asynchronous one?
Somewhere in between? The reality is that both use cases end up
operating in the same way. Both are an inner method that can be
assigned to a variable and passed around as per any other variable.

This implies that using continue or break keywords within the inner
method to refer to the method defining the inner method is
meaningless. This is because the method defining the inner method and
any loop may have been completed before the inner method is invoked.
The same logic holds for using the return keyword to return from the
enclosing method. As a result, FCM does not permit continue or break
from within the inner method, and uses return to return a value from
the inner method.

We did have a concern that the asynchronous use case might get into a
race condition over local variables, so in FCM v0.4 we restricted
local variable access to read-only. However, the synchronous use case
requires full read-write access to local variables. As a result FCM
v0.5 allows full read-write access to local variables, and provides an
annotation to warn if there may be concurrent access to local
variables, which is where the real danger of race conditions lies.

The final point of consideration was the control abstraction syntax.
We established that the control abstraction use case is very different
from the other use cases.

  public void process() {
    Lock lock = ...
    withLock(lock) {
      // code protected by the lock
    }
  }

This example is at face value similar to the synchronous use case -
the block of code is executed immediately and inline. However, there
is a key difference - the block of code cannot be assigned to a
variable within the process method. As a result, there is no way to
pass the block of code to another method in the application - the
block is fixed at this point in the code.

A result of being fixed at this point in the code is that the
semantics of the block are entirely different to blocks which can be
assigned to a variable. The continue and break keywords should refer
to the nearest loop, while the return keyword should refer to the
enclosing method. In other words, it has the full semantics of a
normal Java block. Implementing a mechanism to handle control
abstraction is thus outside the scope of FCM.

Syntax choices

This section is Rationale about our syntax choices.


When writing a proposal like FCM, there are two aspects to consider -
syntax and semantics. Semantics is by far the most important, as it
describes what happens in each scenario, and is the guts of the
proposed change. However, it is almost always necessary to show
examples, and for that you need to use a syntax.

With FCM, we chose to use the # symbol as our key syntactic element.
There were a number of reasons. Firstly, # is unused in Java at
present. This means that there are no conflicts within the parser,
simplifying the proposal. It also means that someone reading the
resulting code doesn't have to think about what the symbol means
(unlike ?, <, > or : for example). The # has been used by Java
developers before however, as it is used in Javadoc.

It has been pointed out that in most, if not all, cases, the parser
could work out the meaning of the code without the use of the symbol
at all. This is true, but a key feature of Java is readability. We
believe that in this case, the extra symbol adds to the readability by
clearly identifying the method syntax. This is important for inner
methods which are likely to be running in another thread, hence the #
acts as a kind of warning.

Our decision on the method type syntax took quite a bit of thought. We
chose a syntax which followed the layout of a Java method declaration
- return type, then arguments in brackets, then any exceptions. We did
consider a number of alternatives. For example, this is one alternate
method type syntax that has some appeal (using the examples from the
method types section):

  #(String, String return int)

  #(String)

  #(File return String throws IOException)

  #(return long)

The last goal for our syntax was to be unambiguous. We didn't want to
introduce any new elements of name shadowing.

Finally, it should be borne in mind that the syntax is relatively
unimportant - the semantics of the proposal are what really matter.

Acknowledgments

This proposal is based on the great work of the BGGA and CICE closure proposals.

Our thanks go to Ricky Clarkson for acting as technical reviewer.

In addition, the authors wish to thank all who contribute to the
lively Java blog and forum community.

Notes

    * References in this document are denoted by square brackets
stating the reference number, optionally followed by a colon
introducing a detail section.
    * The notation used for describing the syntax of new language
elements corresponds to the one used in [1].
    * Language elements not further described in this proposal are
defined in [1].


Changes from v0.4

    * Removed automatic creation of stubs when multiple abstract methods
    * Removed method compounds as a result of removing automatic
creation of stubs
    * Changed local variable access to full read-write in all scenarios
    * Introduced a warning annotation to indicate possible concurrent access
    * Added section on transparency
    * Added semantic choices section


References

   1.

      The Java Language Specification, Third Edition
      http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html
   2.

      The Haskell 98 Reports, Expressions, Section: 3.3 Curried
Applications and Lambda Abstractions
      http://haskell.org/onlinereport/exps.html#sect3.3
   3. B. Lee, D. Lea, J. Bloch: "Concise Instance Creation
Expressions: Closures without Complexity"
      http://docs.google.com/Doc.aspx?id=k73_1ggr36h
   4.

      G. Bracha, N. Gafter, J. Gosling, P. von der Ahé: "Closures for
the Java Programming Language  (v0.5)"
      http://www.javac.info
   5.

      Java™ Platform Standard Ed. 6, Reflection API
      http://java.sun.com/javase/6/docs/api/index.html?java/lang/reflect/package-summary.html
   6. R. D. Tennent: "Principles of Programming Languages"
      Prentice Hall International, Englewood Cliffs, NJ, USA, 1981


More information about the lambda-dev mailing list