Endless Parentheses

Ramblings on productivity and technical subjects.

profile for Malabarba on Stack Exchange

Introducing Names: practical namespaces for Emacs-Lisp

A little over a month ago, I released a package called Names, designed for mitigating Emacs' namespace issue. Before I even had a chance to announced it, it made a bit of a splash on r/emacs, which I've taken to mean that people are interested. I've been holding off on this post until I had a couple of Names-using packages under my belt, so I could actually speak from experience as opposed to expectation, and that's finally the case.

Names aims to provide an implementation of namespaces in Emacs-Lisp with four guiding principles:

Practical
Actually useful and easy to grasp.
Complete
Support any function, macro, or special-form available in emacs-lisp, even the ones defined by you or a third party.
Robust
No-surprises, well-tested, and with clearly stated limitations.
Debuggable
Support edebug, find-function/ variable/ face, eval-defun, and eval-last-sexp (also known as C-x C-e). Integration with other developing tools are under way.

Why a namespace package?

The lack of actual namespaces in elisp is a recurring topic in the Emacs community. The discussion is long and beyond the scope of this post, so I'll refer those interested to Nic Ferrier's great essay on the subject and to a concise explanation by Jon Snader as well.

In short, Emacs takes the approach of prefixing every symbol name with the name of the package. This successfully avoids name clashes between packages, but it quickly leads to code that's repetitive and annoying to write. The links above mentions many shortcomings of this approach, but this code-cluttering repetition of the package name has always bothered me the most. It makes the code longer and more tiresome to read

See this example from package.el. The word “package” is repeated 7 times in a 10-line function.

;;;###autoload
(defun package-initialize (&optional no-activate)
  "[DOC]"
  (interactive)
  (setq package-alist nil)
  (package-load-all-descriptors)
  (package-read-all-archive-contents)
  (unless no-activate
    (dolist (elt package-alist)
      (package-activate (car elt))))
  (setq package--initialized t))

What does Names do?

It doesn't change this approach, nor does it try to revolutionize Emacs or reinvent packages. Names fully adheres to Emacs conventions and is completely invisible to the end-user, it simply gives you (the developer) a convenient way of writing code that adheres to this standard.

Here's what the code above would look like inside a namespace.

;;;###autoload
(define-namespace package-

:autoload
(defun initialize (&optional no-activate)
  "[DOC]"
  (interactive)
  (setq alist nil)
  (load-all-descriptors)
  (read-all-archive-contents)
  (unless no-activate
    (dolist (elt alist)
      (activate (car elt))))
  (setq -initialized t))
)

define-namespace is a macro. At compilation, it expands to generate the exact same code as the original, thus being completely invisible to the user.

How reliable is it?

I'll forgive you for being sceptical, wrapping your entire package in a macro sounds risky. Rest assured, Names is very smart in what it does, and it strives to avoid surprises.

Furthermore, it features a thorough test suite which leverages off other well-tested packages to the respectable sum of 244 tests. Of course, the number by itself has no objective meaning, but it shows that I'm commited to making it as reliable and robust as possible. To prove I mean business, the last two packages I pushed to Melpa already make use of it. And if that's not enough, I just got news that shell-switcher has joined the party and seems to be passing all tests.

In terms of availability, it's on GNU Elpa, so it's a safe dependency for any Emacs starting with 24.1.

And how do I use it?

If you learn better by example, you can have a quick look at this short dummy package available on the repo or, for real world packages, see camcorder.el or aggressive-indent. In any case, it's important to check the Readme for the most up-to-date instructions but here's the gist of it:

  • List names as a dependency. A typical header will contain
;; Package-Requires: ((names "0.5") (emacs "24"))
  • Write all code inside the define-namespace macro, preceded by an ;;;###autoload cookie. The first argument of the macro is the prefix which will be applied to all definitions. require and provide statements go outside the macro.
;; `require' statements come first.
(require 'whatever)

;;;###autoload
(define-namespace example-
;;; Code goes here
)

(provide 'example)
;;; example.el ends here
  • Inside the macro, instead of using ;;;###autoload cookies, use the :autoload keyword.

And that's pretty much it. Just write your code without prefixing every symbol with the package name, and Names will take care of that for you.

1. Every definition gets namespaced

Any definitions inside will have the prefix prepended to the symbol given. So the code

;;;###autoload
(define-namespace foo-

(defvar bar 1 "docs")

:autoload
(defun free ()
  "DOC"
  (message "hi"))
)

essentially expands to

(defvar foo-bar 1 "docs")

;;;###autoload
(defun foo-free ()
  "DOC"
  (message "hi"))

2. Function calls and variables are namespaced if defined

This is best explained by example. This code:

(define-namespace foo-

(defvar var infinite)
(defun infinite (x)
  (infinite x))

(infinite 2) ;; Local function call
(something-else t) ;; External function call
(::infinite var) ;; External function call
infinite ;; Variable
)

expands to this code:

(defvar foo-myvar infinite)
(defun foo-infinite (x)
  (foo-infinite x))

(foo-infinite 2) ;; Local function call
(something-else t) ;; External function call
(infinite foo-var) ;; External function call.
infinite ;; Variable.

Note how:

  • The infinite symbol gets namespaced only as a function name, not when it's used as a variable. That's because Names knows that foo-infinite is not a defined variable.
  • The symbol ::infinite is not namespaced, because it had been protected with ::.
  • something-else is not namespaced, because it is not a locally defined function, so it must be external.

3. Quoted forms are not namespaced.

Whenever a form is not meant for evaluation, it is left completely untouched. The most significant example of this are lists and symbols quoted with a simple quote (e.g. 'foo). These are regarded as data, not code, so you'll have to write the prefix explicitly inside these quoted forms.

Some examples of the opposite:

  • Symbols quoted with a function quote (e.g. #'foo) are regarded as function names, and are namespaced as explained in item 2. Remember you should use the function quote for functions anyway.
  • Comma forms inside a back-tick form (e.g. `(nothing ,@(function) ,variable)) are meant for evaluation and so will be namespaced.

But is it all worth it?

Absolutely! Like I said, I've already written two packages using Names, and I had a blast! Of course, my opinion is biased. But I can say with all honesty that it's an absolute delight to not need to worry about all those prefixes.

I invite people to try it out. If you do, make sure you (require 'names-dev) in your init file. This will enable integration with edebug, find-function, eval-defun, and eval-last-sexp. I've already got news that shell-switcher has made the conversion, and alchemist seems to be on the way.

comments powered by Disqus