Homepage: https://www.gnu.org/software/emacs
Author: Stefan Monnier
Define functions by their inliner
This package provides the macro `define-inline' which lets you define
functions by defining their (exhaustive) compiler macro.
The idea is that instead of doing like defsubst and cl-defsubst (i.e. from
the function's definition, guess the best way to inline the function),
we go the other way around: the programmer provides the code that does the
inlining (as a compiler-macro) and from that we derive the definition of the
function itself. The idea originated in an attempt to clean up `cl-typep',
whose function definition amounted to (eval (cl--make-type-test EXP TYPE)).
The simplest use is for plain and simple inlinable functions. Rather than:
(defmacro myaccessor (obj)
(macroexp-let2 macroexp-copyable-p obj obj
`(if (foop ,obj) (aref (cdr ,obj) 3) (aref ,obj 2))))
Or
(defsubst myaccessor (obj)
(if (foop obj) (aref (cdr obj) 3) (aref obj 2)))
Or
(cl-defsubst myaccessor (obj)
(if (foop obj) (aref (cdr obj) 3) (aref obj 2)))
You'd do
(define-inline myaccessor (obj)
(inline-letevals (obj)
(inline-quote (if (foop ,obj) (aref (cdr ,obj) 3) (aref ,obj 2)))))
Other than verbosity, you get the best of all 3 above without their
respective downsides:
- defmacro: can't be passed to `mapcar' since it's not a function.
- defsubst: not as efficient, and doesn't work as a `gv' place.
- cl-defsubst: only works by accident, since it has latent bugs in its
handling of variables and scopes which could bite you at any time.
(e.g. try (cl-defsubst my-test1 (x) (let ((y 5)) (+ x y)))
and then M-: (macroexpand-all '(my-test1 y)) RET)
There is still one downside shared with the defmacro and cl-defsubst
approach: when the function is inlined, the scoping rules (dynamic or
lexical) will be inherited from the call site.
Of course, since define-inline defines a compiler macro, you can also do
call-site optimizations, just like you can with `defmacro', but not with
defsubst nor cl-defsubst.