Definline functions do not work as higher-order functions when AOT compiled

Description

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))

Environment

OSX 10.8, Linux

Attachments

2
  • 15 Jan 2014, 10:56 AM
  • 27 Jun 2013, 07:44 AM

Activity

Show:

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 slightly smiling face

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.

Declined

Details

Assignee

Reporter

Labels

Patch

Code and Test

Priority

Affects versions

Created June 27, 2013 at 7:44 AM
Updated January 22, 2014 at 9:35 PM
Resolved January 22, 2014 at 9:35 PM

Flag notifications