Java 8 Lambda Interop

1,694 views
Skip to first unread message

Andrew Oberstar

unread,
Jul 26, 2015, 4:33:17 PM7/26/15
to clo...@googlegroups.com
Hi,

I'm wondering if anyone has a good approach for making calls from Clojure to Java APIs (e.g. Stream API) that expect a @FunctionalInterface type.

Ideally, IFn would transparently work, but I'm guessing that requires some compiler changes.

Right now, the best I can think of is a function or macro to reify a wrapper around a Clojure function to implement all of the usual interfaces from java.util.function.

Anyone have any better ideas?

Andrew Oberstar

Gary Verhaegen

unread,
Jul 27, 2015, 5:16:42 AM7/27/15
to clo...@googlegroups.com
You're probably aware of this, but @FunctionalInterface is not a type, it's an annotation. All it does is ensure, at compile time, that the annotated element is an interface with a single non-default and non-static method. At the type-system level, it's just an interface like any other, and the lambda syntax is just a shorthand for an anonymous instance of a well-defined type.

Since the lambda syntax is java-compiler magic, you can't access it from Clojure, and the most straightforward option right now is to actually know which type is expected, e.g.:

user=> (-> (doto (java.util.ArrayList.) (.add 1) (.add 2)) (.stream) (.map (reify java.util.function.Function (apply [_ arg] (inc arg)))) (.collect (java.util.stream.Collectors/toList)))
[2 3]
user=>

As neither IFn nor Function are Clojure protocols, I do indeed think you're best bet is a macro to essentially generate the above reify. You can of course do a single macro that reifies to all of the protocols that you need.

I don't think it's realistic to hope that IFn will cover any arbitrary @FunctionalInterface, as that is Java compiler magic. It may, in the future, be extended to cover all of the standard ones in java.util.function, or even all the ones in the standard library, but it's not going to happen until Java 7 support is dropped. I guess the best you could hope for in the short term would be to have IFn changed to a protocol.

Andrew Oberstar

unread,
Jul 27, 2015, 8:52:47 PM7/27/15
to clo...@googlegroups.com
Thanks for the reply Gary. Sounds like I'm on as good a track as I can be with current Clojure.

I am curious though why you say that it is unrealistic for IFn to support arbitrary @FunctionalInterface. It certainly seems like it would require compiler changes, but I would think that either through emitting bytecode closer to Java 8 lambdas or through some form of type coercion it would possible. For example, Groovy just coerces their Closures to any Single Abstract Method type.

I'm not sure how java.util.function.* as protocols would work, but still would require implementing for each SAM you come across. IFn as a protocol seems to address a different interop use case. Maybe for receiving a Java lambda you want to use as if it's a Clojure function. 

Most of the Java interop from Clojure is slick (sometimes more clear than in Java itself), it would be unfortunate to leave functions as second-class citizens for interop. Granted, there may be a simplicity argument against this (maybe that's why Java varargs require an explicit array?).

Andrew Oberstar

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mikera

unread,
Jul 27, 2015, 9:16:54 PM7/27/15
to Clojure, ajobe...@gmail.com
It could certainly be achieved in the Clojure compiler, by allowing (some-functional-interface .....) to compile to the appropriate function call even if it doesn't implement IFn

It would be quite a big change though and would probably have some limitations, e.g.:
a) It probably wouldn't work with regular vars since it wouldn't be able to handle re-binding
b) You would probably have to type hint the "some-functional-interface" object in some way so that the compiler knows to do this at compile time

A less invasive option would be to just have some code to wrap functional interfaces in an appropriate IFn.

Worth a JIRA ticket for consideration at least?

Andrew Oberstar

unread,
Jul 27, 2015, 9:53:25 PM7/27/15
to Mikera, Clojure
Mikera, I think you're addressing a different interop concern. I'm particularly interested in something like this:

(-> (IntStream/range 0 100) (.filter odd?) (.limit 5) (.collect Collectors/toList))

Where "odd?" is a normal Clojure IFn that I want to use when calling a Java API that expects something implementing a single-method-interface (Predicate in this case).

Right now I need to do something like this:

(defn lambda [f] (reify Predicate (test [x] (f x))))

(-> (IntStream/range 0 100) (.filter (lambda odd?)) (.limit 5) (.collect Collectors/toList))

Andrew Oberstar

Sean Corfield

unread,
Jul 27, 2015, 10:49:11 PM7/27/15
to clo...@googlegroups.com
I think Mike was suggesting something like this:

(-> (IntStream/range 0 100) (.filter ^Predicate odd?) (.limit 5) (.collect Collectors/toList))

and having the Clojure compiler figure out that you’re trying to cast an IFn to a functional interface and therefore "do the magic" for you. I don’t know whether this is even feasible (FWIW, I certainly don’t think it is desirable).

The compiler would have to know about all the possible functional interfaces in order to reify them behind the scenes for you (since each one has a different method name, so something needs to know how to get from the Java interface type to the method name behind it).

I’m not sure I "get" your use case — if you’re dealing with that much Java interop it seems like it might be a good idea to create a DSL (functions, maybe macros) that make the code cleaner and "hide" the low-level details of Java interop anyway. Besides, your example sounds like something Clojure can already do natively and much more cleanly. Can you perhaps give us more detailed motivation for what you’re trying to achieve? Perhaps there’s a cleaner way altogether…?

Sean

Mikera

unread,
Jul 27, 2015, 11:23:27 PM7/27/15
to Clojure, ajobe...@gmail.com
Ah, I get what you are doing now.

Don't think that is likely to work unless Clojure starts making IFn instances implement the right java.util.function.* interfaces. Which seems unlikely given the conservatism of Clojure development. Having said that, I do think it is possible, have been playing around with a little toy language implementation that does something similar.

I think you are better off for now writing a function or macro that allows you to wrap a Clojure function as a java.util.function.Predicate and lets you do something like:

(.filter (predicate odd?))

Colin Fleming

unread,
Jul 28, 2015, 4:52:44 AM7/28/15
to clo...@googlegroups.com
Which seems unlikely given the conservatism of Clojure development.

More than that, it would mean that Clojure required Java 8.

Andrew Oberstar

unread,
Jul 28, 2015, 8:59:55 PM7/28/15
to clo...@googlegroups.com
My use case isn't a particularly great one at that minute. It mainly just seemed like an unfortunate extra step to interacting with Java APIs.

Essentially, I was trying to leverage APIs that return a Java Stream, but wanted to interact with them with Clojure goodness, like map/reduce/transduce. I ended up extending CollReduce to support BaseStream. The lambda interaction is a pretty minimal portion of that code, being a little more heavy in the supporting tests.

Most of my interest is projecting into the future and presuming that more Java APIs will expect SAM arguments. If that is the case, it seems like a beneficial thing to support in Clojure. Though it depends on the impact to the compiler. I can see the argument either way.

Andrew Oberstar

Matan Safriel

unread,
Apr 11, 2017, 12:11:50 PM4/11/17
to Clojure
Sorry to awaken this old thread. It seems like reifying a valid lambda, for the specific clojure function which you want to pass to Java code, is indeed the most sensible way, given all the surface that java's lambda api entails. To the best of my knowledge no generic interop features or libraries have emerged since this thread.
Reply all
Reply to author
Forward
0 new messages