• Java
    August 25, 2006

Closures without Function Types

John Rose

There is a proposal afloat in several places including Javalobby
by some of Java's authors to add closures and function types to Java.
The authors are Gilad Bracha, Neal Gafter, James Gosling, and Peter von
der Ahé, and so I will tag their proposal as BGGA.

For my part, I think Java needs (a) better closures and (b) slightly
better type parameters but not (c) function types. Let‘s examine
the last first…

(c) Not Functions

There are various interesting lesser details to worry over, but the major impact of the BGGA
proposal is to introduce a new subsystem (type scheme) of function
types. It is a thoughtfully designed system, but it overlaps heavily
with a pre-existing and parallel subsystem of interface types. This
overlap with pre-existing functionality robs function types of the
power they would have in another language. (I say this with great
fondness for the functional programming style, as a long time Lisp
programmer and author of an optimizing Scheme compiler.)

In a nutshell, any use case for a function signature U(V,W...) throws X can be addressed by a one-method interface I with a method U I.m(V,W...) throws X.
(There‘s a little more than a nutshell‘s worth; keep
reading.) Thus, whatever value the new type system has must somehow
exceed the utility of one-method interfaces.

The introduction of generic types was also a major impact on the
language, but type parameters compensated for their complexity by
addressing a serious flaw in interface types, a widely felt flaw: List was less workable than List<T>).
There is not a corresponding widely felt need which function types
address, because using one-method interfaces for function types has
been a reasonable workaround.

Here are some benefits (real and perceived) of function types, examined critically in the setting of Java:

Names are Distractions

It‘s easier to manage nameless types, because names can be misleading and sometimes cause useless type mismatches. But
classes offer various workarounds for name management problems. For
example, an API which features a method that takes a functional
parameter can also feature a nested interface to name the parameter's
type: The parameter type need not be anonymous. The most common handful
of types (such as Runnable) can be in java.lang, while other function-like types can be API-specific.
The occasional remaining mismatch can be patched by closure creation or
an adapter generation convention without recourse to function types.

Pure Structure is Concise

Similarly, function types can be concise, since they omit extraneous names. But
the names are not always extraneous. They often convey useful
intentions to the programmer, especially if accompanied by a Java
documentation comment. Even if the function type is pure structure, a
symbolic name can abbreviate it; the name is rarely much longer than
the spelling of the type itself. The name is much longer only with the
simplest types, like Runnable for void().

Easy Mix-and-Match

Identical calling sequences are fully compatible, instead of
being artificially distinguished merely by name. This leads to power
when combining separately designed modules, when they serendipitously
apply to the same function types. But this requires a
system designed for serendipity. Function types that are logically
similar must tend to be identical, or else the system will have to
adapt to the near misses of calling sequence. Java has too many types.
Consider the difference in Java between int and Integer, int and long, throws IOException versus no throws, and varargs versus “vanilla”. Functional type systems tend to avoid such distinctions. Java API designers have too many degrees of freedom to expect to get lucky matching independently authored calling sequences.

Mismatches Easily Patched

Logically similar calling sequences can inter-convert (in BGGA). This can make up near-misses between types of values exchanged by unrelated APIs. But
this is a two-edged sword, because (except at the point of closure
creation) the VM probably has to build an adapter wrapper of some sort,
which becomes an invisible overhead. (Or else function types are
partially erased in the VM, which forces dynamic checking overheads,
compared to strongly typed interfaces.) Better to have calling sequence
conversion happen (a) at closure creation and (b) as an explicit
construct (e.g., cast, new closure, or delegate creation syntax)

...Like Peas and Carrots

Languages with closures (Haskell, etc.) generally integrate them
with function types; it is a well-understood paradigm. Definitions of
the term closure tend to specify that a closure is a function. But
two of the oldest languages with closures, Scheme and Smalltalk, do not
have a system of function types per se. (Scheme has just one type, PROCEDURE,
which covers both closures and intrinsics. Smalltalk calls its closures
“blocks“, and manipulates them as — what else?
— one method objects.) Truly closure-like referential
transparency without function types can be observed in more recent
languages also, notably Beta and Java itself, whose inner classes act
as limited closures. In its essence, a closure is a source-language
construct which provides a runtime handle to a snippet of code. The
“closing“ of a closure refers to the fact that the code
snippet has full and seamless access to the source code around the
closure. The code inside the snippet is “in synch” with the
code outside; the same names mean the same things. This concept is
about factoring source code, not about type systems. There is surely a
type system to help classify the snippet handles, but interfaces can do
that job as well as pure function types.

(Your Point Here)

(Function types surely provide other advantages which I‘ve
overlooked. I‘m also sure those advantages are diluted in the
setting of Java.)

These various advantages, properly weighed, do not add up to a
mandate for a new type subsystem, as long as the crucial advantage
(better closures) can be obtained without the new types. There is much
to like about BGGA including concise closure creation, better
closure semantics, and local functions. But the extra types don‘t
really carry their weight.

Join the discussion

Comments ( 1 )
  • Neal Gafter Saturday, August 26, 2006
    There are a number of things you can express with closures that you can't express with interfaces and classes. Without function types, you can't write APIs that provide exception transparency. See http://gafter.blogspot.com/2006/08/use-cases-for-closures.html for what this means.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.