Read "people wouldn't stop harassing us for lambdas after we decided to delay them again and again, and so we half-assed a solution that is easy for us and binds into all the overengineered class hierarchies you have lying around".
The linked talk actually does a pretty good job describing how Java 8's Lambda is both backwards compatible and forward thinking.
1. The idea of @FunctionalInterface leveraged practices that were already in use--interfaces with a single method--for Comparator, Runnable, Callable, and external libraries. This meant that users could use lambdas to implement such interfaces immediately, with no changes to existing libraries.
2. The actual implementation of a lambda in bytecode is a single invokedynamic call. They went that route to avoid tying the bytecode representation to their initial implementation.
A future change could add true function types, and have them automatically instantiate a target functional interface (provided that they have the same "shape"). That would require a language change (adding function types), but not a bytecode change.
That would have the nice result that all the code that people are writing now could work with new function types, and new code could be written to accept the function types instead of functional interfaces. My library would become unnecessary at that point, and I would be pretty happy about that.
That is, Java 8's lambda implementation does not preclude fully-backwards-compatible "real" lambdas in the future.
Personally, I think the Lambda project was a well-reasoned, intelligent step forward. The video is really worth watching, because I wasn't aware of how flexible their future directions are until I watched it.
> That is, Java 8's lambda implementation does not preclude fully-backwards-compatible "real" lambdas in the future.
No, it just made them a lot harder to achieve, because any decent "real" lambda implementation requires now the introduction of another language feature: type aliases.
Our company has been slowly converting a medium-sized (~100K LOC) Java codebase to take advantage of some Java 8 features. One example is a utility method that accepts a String DB query and then executes a Consumer<ResultSet> for each resulting row. The utility method creates the connection, uses try-with-resources to ensure that it's properly closed, etc. This makes a lot of code a lot cleaner.
But unfortunately, many ResultSet operations can throw SQLException. So we created a `ConsumerE<T, E extends Exception>` to use in such cases, so that we can allow some lambdas to throw exceptions where it is sensible (InterruptedException, SQLException, and IOException being the major culprits).
Pretty soon we ended up with FunctionE, BiFunctionE, and so on.
At the same time, we found a need for more interfaces, such as ObjIntFunction<T, R>, that are not provided by JDK 8.
Rather than continuing to implement these on an as-needed basis, I decided to just solve the problem once-and-for-all by creating this library.
This was perhaps a 20-hour project, and we're already using it in our Java codebase. Are you instead recommending a Scala rewrite for a 100,000-line production system?
I had to register for this. You have pretty much tried to invent Either via code generation. I suggest you look at functionaljava's Either for further reference.
Code generation is in my experience a clear indication that the problem you have has not really been understood.
It's Java's take on this. Java's version is somewhat longer, however.
public delegate void Action<in T1>(T1 arg1);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate TRet Func<out TRet>();
public delegate TRet Func<in T1, out TRet>(T arg1);
public delegate TRet Func<in T1, in T2, out TRet>(T1 arg1, T2 arg2);
public delegate TRet Func<in T1, in T2, in T3, out TRet>(T1 arg1, T2 arg2, T3 arg3);
Yes, that's a huge number. That's why I split it up the way I did (https://github.com/mintern-java/functions#sensible-packaging). If you don't care about `short`, that cuts out nearly 6,000 of the classes. If you don't need three-argument functions, that's another 9,000+ classes.
That leaves 1,316 classes. That's still a lot of classes, unfortunately, but at least it's in the realm of being reasonable.
Does someone know of a good tree-shaking packager for Java? Something that builds a jar (or a directory) with only the needed .class files (or even better, versions of the .class files with the used functions)?
Looks like it would perfectly complement this project.
It may be! It would be pretty cool to do something like:
import net.mintern.functions.auto.ObjCharToBool;
and have the generate-classes stage be able to see that import and automatically generate the requisite interface. Of course, the IDE might not be happy until the first compile, but that seems tolerable.
It seems like Project Lombok has some similar issues... I'll have to see how it handles them.
That would also allow an arbitrary function signature, though I personally think it starts to be better to write your own @FunctionalInterface for readability reasons once you hit more than 3 arguments.
I do hope I am wrong; but isn't this like creating a version of Map for every possible permutation of the primitive types?
I.e. MapIntInt MapIntShort MapShortBool etc. because you cannot write Map<int, short> and somehow think that the overhead of boxing (Map<Integer, Short>) is unbearable.
It's exactly like that. For some use cases, that cost matters. Trove is a popular project that does just what you describe for Set, List, and Map.
For this library, if you stick to binary-core, you basically get what Java 8 provides out of the box, while also getting exception-throwing functional interfaces, static `unchecked` functions for concisely converting from checked to unchecked versions, and `bind` methods for providing currying operations.
So the full type enumeration is one part of it, but there`s a little more to it than that. I probably could have done a better job touting the other benefits.
Omg. Java is getting crippled. The language was never designed with lambda's in mind. And now we have Java 8 with "not-really-lambdas" But some kind of interface magic. Its just ugly as hell.
42 comments
[ 2.9 ms ] story [ 85.7 ms ] thread1. The idea of @FunctionalInterface leveraged practices that were already in use--interfaces with a single method--for Comparator, Runnable, Callable, and external libraries. This meant that users could use lambdas to implement such interfaces immediately, with no changes to existing libraries.
2. The actual implementation of a lambda in bytecode is a single invokedynamic call. They went that route to avoid tying the bytecode representation to their initial implementation.
A future change could add true function types, and have them automatically instantiate a target functional interface (provided that they have the same "shape"). That would require a language change (adding function types), but not a bytecode change.
That would have the nice result that all the code that people are writing now could work with new function types, and new code could be written to accept the function types instead of functional interfaces. My library would become unnecessary at that point, and I would be pretty happy about that.
That is, Java 8's lambda implementation does not preclude fully-backwards-compatible "real" lambdas in the future.
Personally, I think the Lambda project was a well-reasoned, intelligent step forward. The video is really worth watching, because I wasn't aware of how flexible their future directions are until I watched it.
No, it just made them a lot harder to achieve, because any decent "real" lambda implementation requires now the introduction of another language feature: type aliases.
But unfortunately, many ResultSet operations can throw SQLException. So we created a `ConsumerE<T, E extends Exception>` to use in such cases, so that we can allow some lambdas to throw exceptions where it is sensible (InterruptedException, SQLException, and IOException being the major culprits).
Pretty soon we ended up with FunctionE, BiFunctionE, and so on.
At the same time, we found a need for more interfaces, such as ObjIntFunction<T, R>, that are not provided by JDK 8.
Rather than continuing to implement these on an as-needed basis, I decided to just solve the problem once-and-for-all by creating this library.
Why? Even if a rewrite/translation of such a small system was perfectly possible, it's not necessary because of the excellent Scala -> Java interop.
Code generation is in my experience a clear indication that the problem you have has not really been understood.
I took a look functionaljava, and it doesn't provide the following:
For that, you would need to have a signature like: That's exactly what this library gives you.That leaves 1,316 classes. That's still a lot of classes, unfortunately, but at least it's in the realm of being reasonable.
Looks like it would perfectly complement this project.
It's been a long time I haven't used it but back in the days I used it for years as part of the build chain and it was pretty flawless.
As I understand it nowadays it's even used for Android builds.
It seems like Project Lombok has some similar issues... I'll have to see how it handles them.
That would also allow an arbitrary function signature, though I personally think it starts to be better to write your own @FunctionalInterface for readability reasons once you hit more than 3 arguments.
I.e. MapIntInt MapIntShort MapShortBool etc. because you cannot write Map<int, short> and somehow think that the overhead of boxing (Map<Integer, Short>) is unbearable.
I can maybe see an extra variation for exceptions, but that would be one more generic or a Set<? extends Exception> no?
For this library, if you stick to binary-core, you basically get what Java 8 provides out of the box, while also getting exception-throwing functional interfaces, static `unchecked` functions for concisely converting from checked to unchecked versions, and `bind` methods for providing currying operations.
So the full type enumeration is one part of it, but there`s a little more to it than that. I probably could have done a better job touting the other benefits.