1

So the functional programmer in me likes languages like python that treat functions as first class citizens. It looks like Java 8 caved to the pressure and "sort of" implemented things like lambda expressions and method references.

My question is, is Java on its way to using first class functions, or is this really just syntactic sugar to reduce the amount of code it takes to implement anonymous classes / interfaces such as runnable? (my gut says the latter).

My ideal scenario

Map<String,DoubleToDoubleFunc> mymap = new HashMap<String,DoubleToDoubleFunc>();
...
mymap.put("Func 1", (double a, double b) -> a + b);
mymap.put("Func 2", Math::pow);
...
w = mymap.get("Func 1")(y,z);
x = mymap.get("Func 2")(y,z);
C.B.
  • 8,096
  • 5
  • 20
  • 34

4 Answers4

7

There is no function structural type in Java 8. But Java has always had first class objects. And in fact, they have been used as an alternative. Historically, due to the lack of first-class functions, what we have done so far is to wrap the function inside an object. These are the famous SAM types (single abstract method types).

So, instead of

function run() {}
Thread t = new Thread(run);

We do

Runnable run = new Runnable(){ public void run(){} };
Thread t = new Thread(run);

That is, we put the function inside an object in order to be able to pass it around as a value. So, first class objects have been an alternative solution for a long time.

The JDK 8 simply makes implementing this concept simpler, and they call this type of wrapper interfaces "Functional Interfaces" and offer some syntactic sugar to implement the wrapper objects.

Runnable run = () -> {};
Thread t = new Thread(run);

But ultimately, we are still using first-class objects. And they have similar properties to first-class functions. They encapsulate behavior, they can be passed as arguments and be returned as values.

In the lambda mailing list Brian Goetz gave a good explanation of some of the reasons that motivated this design.

Along the lines that we've been discussing today, here's a peek at where we're heading. We explored the road of "maybe lambdas should just be inner class instances, that would be really simple", but eventually came to the position of "functions are a better direction for the future of the language".

This exploration played out in stages: first internally before the EG was formed, and then again when the EG discussed the issues. The following is my position on the issue. Hopefully, this fills in some of the gaps between what the spec currently says and what we say about it.

The issues that have been raised about whether lambdas are objects or not largely come down to philosophical questions like "what are lambdas really", "why will Java benefit from lambdas", and ultimately "how best to evolve the Java language and platform."

Oracle's position is that Java must evolve -- carefully, of course -- in order to remain competitive. This is, of course, a difficult balancing act.

It is my belief that the best direction for evolving Java is to encourage a more functional style of programming. The role of Lambda is primarily to support the development and consumption of more functional-like libraries; I've offered examples such as filter-map-reduce to illustrate this direction.

There is plenty of evidence in the ecosystem to support the hypothesis that, if given the tools to do so easily, object-oriented programmers are ready to embrace functional techniques (such as immutability) and work them into an object-oriented view of the world, and will write better, less error-prone code as a result. Simply put, we believe the best thing we can do for Java developers is to give them a gentle push towards a more functional style of programming. We're not going to turn Java into Haskell, nor even into Scala. But the direction is clear.

Lambda is the down-payment on this evolution, but it is far from the end of the story. The rest of the story isn't written yet, but preserving our options are a key aspect of how we evaluate the decisions we make here.

This is why I've been so dogged in my insistence that lambdas are not objects. I believe the "lambdas are just objects" position, while very comfortable and tempting, slams the door on a number of potentially useful directions for language evolution.

As a single example, let's take function types. The lambda strawman offered at devoxx had function types. I insisted we remove them, and this made me unpopular. But my objection to function types was not that I don't like function types -- I love function types -- but that function types fought badly with an existing aspect of the Java type system, erasure. Erased function types are the worst of both worlds. So we removed this from the design.

But I am unwilling to say "Java never will have function types" (though I recognize that Java may never have function types.) I believe that in order to get to function types, we have to first deal with erasure. That may, or may not be possible. But in a world of reified structural types, function types start to make a lot more sense.

The lambdas-are-objects view of the world conflicts with this possible future. The lambdas-are-functions view of the world does not, and preserving this flexibility is one of the points in favor of not burdening lambdas with even the appearance of object-ness.

You might think that the current design is tightly tied to an object box for lambdas -- SAM types -- making them effectively objects anyway. But this has been carefully hidden from the surface area so as to allow us to consider 'naked' lambdas in the future, or consider other conversion contexts for lambdas, or integrate lambdas more tightly into control constructs. We're not doing that now, and we don't even have a concrete plan for doing so, but the ability to possibly do that in the future is a critical part of the design.

I am optimistic about Java's future, but to move forward we sometimes have to let go of some comfortable ideas. Lambdas-are-functions opens doors. Lambdas-are-objects closes them. We prefer to see those doors left open.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
  • Thanks for quoting the mail from Brian. Great read! – asgs Aug 08 '16 at 09:47
  • I must be missing something. The examples show that lambdas compile into objects. Yet Brian is saying "It's a good job we made lambdas-as-functions instead of lambdas-as-objects". So if the above is lambdas-as-functions, could someone please show what lambdas-as-objects code might look like (the discarded approach)? – joeytwiddle Aug 11 '16 at 04:20
  • 1
    @joeytwiddle I think Brian is probably talking about the instrumentation of lambdas at the JVM level. When he says "lambdas-as-objects" he's probably referring to instrumenting lambdas as inner classes. If they did that, they'd probably make it really hard to support actual function types in the future. However they didn't do that to instrument lambdas, [they used invokeDynamic and a few other tricks](http://medianetwork.oracle.com/video/player/1113272510001) and it is implemented such that if the JVM ever supports structural function types, their solution can be adapted to that as well. – Edwin Dalorzo Aug 11 '16 at 16:20
2

They are not really first-class functions IMO. Lambda is ultimately an instance of a functional interface. But it does give you advantages of a first-class function. You can pass it as an argument to a method that expects an instance of functional interface. You can assign it to a variable, in which case its type will be inferred using target typing. You can return it from a method, so on.

Also, lambdas doesn't completely replace anonymous classes. Not all anonymous classes can be converted to lambdas. You can go through this answer for a nice explanation about difference between the two. So, no lambdas are not syntactic sugar for anonymous classes.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
1

It's not syntactic sugar for anonymous classes; what it compiles to is somewhat more involved. (Not that it would be wrong to do that; any language will have its own implementation detail for how lambdas are compiled, and Java's is no worse than many others.)

See http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html for the full, nitty-gritty details.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • The `invokedynamic` implementation seems pretty interesting as treating the construction of the functional interface as an API. I would think that this would cause the run-time evaluation to cause performance issues - but I guess I will have to keep reading as to why they aren't incurred. :) – C.B. Mar 26 '14 at 15:29
1
interface DoubleDoubleToDoubleFunc {
    double f(double x, double y);
}

public static void main(String[] args) {
    Map<String, DoubleDoubleToDoubleFunc> mymap = new HashMap<>();
    mymap.put("Func 1", (double a, double b) -> a + b);
    mymap.put("Func 2", Math::pow);
    double y = 2.0;
    double z = 3.0;
    double w = mymap.get("Func 1").f(y,z);
    double v = mymap.get("Func 2").f(y,z);
}

So it still is syntactic sugar and only to some degree.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138