liveBook · Manning

Inlining

As explained in Clojure: The Essential Reference

Inlining is a compilation technique that substitutes an alternative implementation for a var invocation instead of using its root or thread binding.

Inlining lets the compiler substitute an alternative implementation at a direct var invocation. A var’s :inline metadata supplies that implementation; :inline-arities can limit which argument counts use it. Where inlining applies, it takes precedence over a thread binding or root binding. A call at an unsupported arity uses the ordinary binding instead. section 16.0

Use definline when a function’s inline form is effectively its body: it defines the function and attaches an :inline implementation without writing both separately. Its declaration needs at least a parameter vector, as in (definline f [] ...); (definline f) is invalid. The form returns a var. Direct calls can expand like macros, but passing the function to a higher-order operation uses it as an ordinary function. section 4.5

(definline sq [x]                             ; #1
  `(let [x# ~x] (* x# x#)))

(defn direct-use [x]                          ; #2
  (sq x))

(defn higher-order-use [xs]                   ; #3
  (map sq xs))

(direct-use 2.0)
;; 4.0

(first (higher-order-use [2.0]))
;; 4.0

Compare (sq x) with (map sq xs). The direct call uses the inline form, whose generated local binding evaluates x once; map receives the function value. section 4.5

(ns math-lib-client
  (:require [math-lib :as m]))

(defn vsum [x xs]
  (map #(m/plus (int x) (int %)) xs))

(vsum 3 [1 2 3]) ; #1
;; int plus(int int)
;; int plus(int int)
;; int plus(int int)
;; (4 5 6)

Java interop is a practical reason to inline. With plus defined by definline, calls using (int x) and (int %) select IntegerMath/plus(int, int); the ordinary function wrapper in the earlier example selected the Object overload even with those conversions at the call site. section 4.5

Keep the ordinary function and inline form consistent: an inline-only change can make direct and higher-order calls return different results. definline remains marked experimental as of Clojure 1.10; versions before 1.6.0 had a serious AOT-compilation issue. section 4.5

How Clojure: The Essential Reference covers it

How deep it goes
Explains var precedence, definline expansion, call behavior, and a compiled interop example; does not develop a general compiler implementation.
Examples
Compares direct calls with map, then contrasts Object and primitive Java overloads through an inlined wrapper.
What it assumes
Assumes familiarity with Clojure functions, vars, macros, and Java method calls while demonstrating definline expansion.
What you can do after
Apply it via definline to expose a direct-call Java interop implementation while retaining ordinary function use.
How it connects
Connects inlining to var metadata, root and thread bindings, macro expansion, higher-order functions, type hints, and Java interoperability.

Also explained in

Compare how 4 books explain inlining →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.