7

Is there a way to define a macro in an emacs-lisp file in such a way that the macro would not be visible anywhere else once the file is read and evaluated?

Kind of like a temporary macro that goes out of scope at the end of a file?

Kirill
  • 1,019
  • 7
  • 19
  • 1
    Besides the general solution of using `macrolet`, remember that it is often the case that you need a particular macro only at byte-compile time. In that case, byte-compiling a file without the macro definition loaded, or just using that file without compiling it, will mean that the code in that file does not have the macro available at runtime. IOW, it knows nothing about the macro. – Drew Jan 29 '15 at 06:08
  • 1
    @Drew Oh, so like `(eval-when-compile (defmacro ...))`? – Kirill Jan 29 '15 at 07:33
  • 2
    More like `(eval-when-compile (require 'my-macros))` for the files that you want to see the macro definition. – Drew Jan 29 '15 at 14:43

1 Answers1

11

You can always unbind the macro when the file ends. As far as I know, Emacs Lisp doesn't have to worry about asynchronous complexities; the flow of execution is purely linear. Therefore, you can do this:

(defmacro my-test-macro (secret sauce recipe)
   ;; super secret stuff here
   ;; wouldn't want it to LEAK out
   ;; and get stolen by some other files
   )

;; [...] blabitty blah blah [...]

(fmakunbound 'my-test-macro)

The other solution you should look at (which is less hacky) uses cl-macrolet, which is basically a let for macros.

(eval-when-compile (require 'cl-lib))
(cl-macrolet ((my-test-macro (secret sauce recipie)
                 ;; super secret stuff here
                 ;; wouldn't want it to LEAK out
                 ;; and get stolen by some other files
                 ))

  ;; [...] blabitty blah blah [...]

  )

No doubt, this solution is cleaner, although you pay for it by adding another level of indentation. Also, the macro is dynamically bound and could in theory be run asynchronously (although Emacs is a long way from multi-threading).

PythonNut
  • 10,243
  • 2
  • 29
  • 75
  • I think the first solution won't work in interpreted code... – politza Jan 29 '15 at 21:53
  • 1
    @politza pasting `(defmacro abc () (message "abc")) (abc) (fmakunbound 'abc) (abc)` into `*scratch*` and doing `M-x` `eval-buffer` works for me. You first get "abc" then a `symbol's function definition is void abc` – PythonNut Jan 29 '15 at 23:34
  • 1
    Yes, since Emacs 24.3 introduced eager macro expansion, which I have just discovered. – politza Jan 29 '15 at 23:59
  • ...though I don't know, if this is guaranteed to always happen. – politza Jan 30 '15 at 00:08