performanceclojuremacros

Inlining a function with Clojure macros


More out of curiousity that anything else (but with the expectation that it might occasionally be a useful trick for performance tuning), is it possible to use Clojure macros to "inline" an existing function?

i.e. I would like to be able to do something like:

(defn my-function [a b] (+ a b))

(defn add-3-numbers [a b c] 
  (inline (my-function 
    a 
    (inline (my-function 
      b 
      c)))))

And have it produce (at compile time) exactly the same function as if I had inlined the additions myself, such as:

(defn add-3-numbers [a b c] 
  (+ a (+ b c)))

Solution

  • In case you didn't know, you can define inlined functions using definline

    (doc definline)
    -------------------------
    clojure.core/definline
    ([name & decl])
    Macro
      Experimental - like defmacro, except defines a named function whose
      body is the expansion, calls to which may be expanded inline as if
      it were a macro. Cannot be used with variadic (&) args.
    nil
    

    Also checking the source,

    (source definline)
    -------------------------
    (defmacro definline
      [name & decl]
      (let [[pre-args [args expr]] (split-with (comp not vector?) decl)]
        `(do
           (defn ~name ~@pre-args ~args ~(apply (eval (list `fn args expr)) args))
           (alter-meta! (var ~name) assoc :inline (fn ~name ~args ~expr))
           (var ~name))))
    

    definline simply defines a var with meta-data {:inline (fn definition)}. So although its not exactly what you were asking but you can rebind the var with new metadata to get inlined behavior.