Closures without Function Types

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.

Comments:

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.

Posted by Neal Gafter on August 26, 2006 at 03:50 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

John R. Rose

Java maven, HotSpot developer, Mac user, Scheme refugee.

Once Sun and present Oracle engineer.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today