13

I have been trying to understand why the JDK 8 Lambda Expert Group (EG) decided not to include a new function type into the Java programming language.

Going over the mailing list I found a thread with the discussion about removal of function types.

Many of the statements are ambiguous to me, maybe because of the lack of context and in some cases because of my limited knowledge on the implementation of type systems.

However, there are a couple of questions that I believe I can safely formulate in this site to help me understand better what they meant.

I know I could ask the questions in the mailing list, but the thread is old and all decisions are already made, so chances are that I will be ignored, above all seeing that these guys are already getting delayed with their plans.

In his answer to support the removal of function types and endorse the use of SAM types, Brian Goetz says:

No reification. There was a long thread about how useful it would be for function types to be reified. Without reification, function types are hobbled.

I couldn't find the thread he mentions. Now, I can understand that the introduction of a structural function type may imply certain complications in the Java mostly nominal type system, what I cannot understand is how parametrized SAM types are different in terms of reification.

Aren't they both subject to the same reification problems? Does anyone understands how function types are different than parametrized SAM types in terms of reification?

In another comment Goetz says:

There are two basic approaches to typing: nominal and structural. The identity of a nominal is based on its name; the identity of a structural type is based on what it is composed of (such as "tuple of int, int" or "function from int to float".) Most languages pick mostly nominal or mostly structural; there are not a lot of languages that successfully mix nominal and structural typing except "around the edges." Java is almost entirely nominal (with a few exceptions: arrays are a structural type, but at the bottom there is always a nominal element type; generics have a mix of nominal and structural too, and this is in fact part of the source of many of people's complaints about generics.) Grafting a structural type system (function types) onto Java's nominal type system means new complexity and edge cases. Is the benefit of function types worth this?

Those of you with experience in the implementation of type systems. Do you know any examples of these complexities or edge cases that he mentions here?

Honestly I get confused by these allegations when I consider that a programming language like Scala, which is entirely based on the JVM, has support for structural types like functions and tuples, even with the reification problems of the underlying platform.

Don't get me wrong, I am not saying that a function type should be better than the SAM types. I just want to understand why they took this decision.

8
  • 5
    "Grafting a structural type system (function types) onto Java's nominal type system means new complexity" - this most likely was meant as complexities for users having to learn more to use the language. Something like simple and easy to use Java becomes like complex and hard to learn C++, stuff like that. Mailing list members could likely allude to criticism of prior "big wave of improvements", one prominent example being Java 5 sucks article by Clinton Begin of MyBatis
    – gnat
    Jan 11, 2013 at 18:01
  • 3
    "Simple and easy to use Java becomes like complex and hard to learn C++": Unfortunately, one problem with mainstream programming languages is that they have to retain backward compatibility and therefore they tend to become too complex. So IMHO (having many years experience with both C++ and Java) there is something true to this statement. I often get the feeling that Java should be frozen and a new language should be developed instead. But Oracle cannot take such a risk.
    – Giorgio
    Jan 11, 2013 at 18:58
  • 4
    @edalorzo: As Clojure, Groovy, Scala and other languages showed, it is possible to (1) define a new language (2) use all the wealth of libraries and tools that is already written in Java without breaking backward compatibility (3) give Java programmers the freedom to choose whether they want to stay with Java, switch to another language, or use both. IMO, expanding an existing language indefinitely is a strategy for keeping customers, in the same way as mobile phone companies need to create new models all the time.
    – Giorgio
    Jan 11, 2013 at 19:24
  • 2
    As I understood from SAMbdas in Java, one of the reasons is that it would be problematic to keep some libraries (collections) backward compatible.
    – Petr
    Jan 11, 2013 at 21:17
  • 2
    Related post on SO which links to this other post by Goetz.
    – assylias
    Jan 14, 2013 at 23:12

1 Answer 1

6

The SAM approach is actually somewhat similar to what Scala (and C++11) do with anonymous functions (created with Scala's => operator or C++11's []() (lambda) syntax).

The first question to answer on the Java side is whether to have the return type of a lambda statement be a new primitve type, like int or byte, or an object type of some sort. In Scala, there are no primitive types -- even an integer is an object of class Int -- and functions are no different, being objects of class Function1, Function2, and so forth, depending on the number of arguments the function takes.

C++11, ruby and python similarly have lambda expressions which return an object which is callable in explicit or implicit way. The returned object has some standard method (such as #call which can be used to call it as a function. C++11, for instance, uses a std::function type which overloads operator() so that calls of the object's call method even look like function calls, textually. :-)

Where the new proposal for Java gets messy is in the use of structural typing to allow assigning such a method to another object, such as a Comparator which has a single main method with a different name. While this is conceptually icky in some ways, it does mean that the resulting object can be passed to existing functions which take an object to represent a callback, a comparator, and so on, and expect to be able to call a well-defined single method such as #compare or #callback. C++'s trick of overriding operator() neatly dodged this issue even before C++11, since all such callback options were callable the same way, and thus the STL required no adjustment to allow C++11 lambdas to be used with sort and so on. Java, not having used a standard naming for such objects in the past (perhaps because no trick like operator overloading made a single approach obvious), isn't so lucky, so this hack prevents them from having to change a whole lot of existing APIs.

2
  • 2
    Ironically, the problem if having a myriad of different, incompatible types, all representing some kind of "function-like thing" could have neatly avoided by adding standard function types to the library early on, independent of having actual literal syntax for constructing them. If there had been a java.util.Function2<T1, T2, R> in Java 1.0, then there would be no Comparator interface, instead all those methods would take a Function2<T1, T2, int>. (Well, there would have be a whole swath of interfaces such as Function2TT_int<T1, T2> because of primitives, but you get my point.) Nov 3, 2016 at 4:32
  • 1
    @JörgWMittag however, they deliberately avoided such generic types as Pair because they wanted types to have meanings, like "something which compares objects"
    – user253751
    Oct 11, 2021 at 12:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.