42 comments

[ 2.9 ms ] story [ 85.7 ms ] thread
It's so sad that this is necessary. Proper function types would have made lambdas so much more useful.
I agree wholeheartedly with your first sentence, but I still find lambdas to be quite useful in spite of their limitations.
I find them useful as well. But their usefulness is just not on the level of closures in functional languages, and it makes me sad.
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".
I believe Hacker News calls this a Minimum Viable Product.
MVP and 100% backward compatibility don't go with each other well.
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.

Thanks. But that is totally compatible with what I said. Java lambdas were well thought out, doing MVP style development would highly likely blow 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.

It is not necessary - just use the "boxed" version of the primitive types.
(comment deleted)
What is the use for this?
With Java 8 you can create anonymous functions, but they have to conform to a pre-existing interface. This is a huge collection of those interfaces.
(comment deleted)
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.

Do you know about Scala?
Yes.
It sort of seems like you've spent a lot of effort to re-invent a less good version of scala? Why not Scala?
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?
> Are you instead recommending a Scala rewrite for a 100,000-line production system?

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.

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.

I understand Either, and I would absolutely prefer to be using it. That was going to be one of my follow up projects, and it would depend on this one.

I took a look functionaljava, and it doesn't provide the following:

    Either<IOExeption, String> line = Either.from(reader::getLine);
For that, you would need to have a signature like:

    static <E extends Exception, R> Either<E, R> from(NilToObjE<R, E> f);
That's exactly what this library gives you.
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);
Over 16,000 classes. Better increase your PermGen.
No more PermGen in Java 8. Besides, you only pay for the classes you use.
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.

not all gets loaded, only the classes you use.
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.

Proguard exists since many years and does optimization, shrinking, unused classes and unused methods removal etc. Even obfuscation if you want.

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.

Last I checked (~3 months ago) Proguard was not Java 8 compatible, at least according to their website.
Proguard 5.x supports Java 8.
Isn't possible to generate those like on-demand? That would make those 16k classes availables to use, but present only when you need them.
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.

Yeah I'm confused here too, I understand there might be a need for some of these, but you should be able to just rely on autoboxing of primitives to Objects like what RxJava does: https://github.com/ReactiveX/RxJava/tree/1.x/src/main/java/r...

I can maybe see an extra variation for exceptions, but that would be one more generic or a Set<? extends Exception> no?

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.