Definline functions do not work as higher-order functions when AOT compiled
Description
Environment
OSX 10.8, Linux
Attachments
- 15 Jan 2014, 10:56 AM
- 27 Jun 2013, 07:44 AM
Activity
Tassilo Horn January 22, 2014 at 9:35 PM
The root cause of this issue is https://clojure.atlassian.net/browse/CLJ-1330#icft=CLJ-1330 as investigated by Nicola Mometto in the thread cited above: http://dev.clojure.org/jira/browse/CLJ-1330
So fixing the latter will fix this one, too.
Tassilo Horn January 22, 2014 at 8:58 AM
Alex, definline
's intention is to be considered like defmacro in case of (my-definline x)
where it saves one function call, but to be callable as a normal function in higher-order scenarios like (map my-definline [x y z]). Therefore, it expands to a normal defn
form with :inline
metadata which is a macro that's used by the compiler.
Wether it should be removed in the future is a separate issue. It is there right now, and people use it. The consequence of this bug is that you cannot trust AOT-compiled code, because there might be some dependency that uses definline. Additionally, all clojure.core definlines (booleans, bytes, chars, shorts, floats, ints, doubles, longs) are broken when applied as HOF, because clojure itself is always distributed in AOT-compiled form.
Really, it would be grossly negligent to release Clojure 1.6 knowing of this bug.
I've written a more detailed mail to the clojure-dev list:
https://groups.google.com/d/msg/clojure-dev/UeLNJzp7UiI/UCyVvYLfHWMJ
Colin Fleming January 21, 2014 at 11:28 PM
Fair enough, although it's been experimental for five major releases now. If we're convinced it's a failed experiment (and if it can't be relied on to create a function, it pretty much is since you might as well use a macro) I'd argue that deprecation is justified and sends a stronger signal that it doesn't actually work as intended. But either way, I'm certainly convinced now
Alex Miller January 21, 2014 at 10:20 PM
Sorry about that. I will now split some definitional hairs by saying that definline is marked as "experimental" to indicate it may or may not even become a feature whereas "deprecation" indicates that it was a feature which you should no longer use. I think in this case they serve the same functional intent which is to warn you away from relying on it as part of the language.
Colin Fleming January 21, 2014 at 8:20 PM
This is a little disappointing since there's really no alternative right now, and I'm assuming that this sort of advanced optimisation is a ways off. If this is the plan, I'd definitely recommend marking definline as deprecated and making clear in the docstring that it shouldn't be relied on to return a function. Currently it states: "like defmacro, except defines a named function...", and based on this ticket that is clearly people's expectation.
See discussion on Clojure group: https://groups.google.com/d/topic/clojure/v0ipoiP8X1o/discussion
Functions defined with definline appear to misbehave when AOT compiled and used with higher-order functions - it seems like the macro function is stored instead of the expansion. I've attached a small test project here reproducing the issue. It can be run with:
lein compile lein uberjar java -jar target/aot-test-0.1.0-SNAPSHOT-standalone.jar
The relevant part of the test namespace is:
(definline java-list? [element] `(instance? List ~element)) (defn -main [& args] (println (java-list? 2)) (println ((var-get #'java-list?) 2)) (println (filter java-list? [1 2 3])) (println (map java-list? [1 2 3])))
The output produced is:
false (clojure.core/instance? java.util.List 2) (1 2 3) ((clojure.core/instance? java.util.List 1) (clojure.core/instance? java.util.List 2) (clojure.core/instance? java.util.List 3))