An implementation of namespaces for Emacs Lisp. Helps you keep the global namespace clean and protect your symbols from clobbering.
Pull requests welcome.
Requires Emacs 24.3 or later.
;; Open a namespace:
(namespace agent
;; Require and autoload some elisp features:
:use
[ cl
gnus
(agent-mode turn-on-agent-mode)
(spy-training plant-explosives daring-escape) ])
;; Create a private variable:
(def real-name "Bond, James Bond")
;; Get the variable's value:
(@ real-name)
; => "Bond, James Bond"
;; Create a variable you can change:
(defmutable cover)
;; Update a mutable variable:
(@set cover "David Somerset")
;; Define a private function:
(defn identify ()
(concat "Hello, I'm " (@ cover) "."))
;; Call a private function:
(_ identify)
; => "Hello, I'm David Somerset"
;; Quote a private symbol:
(add-hook 'border-crossed-hook (~ identify))
;; Make some values public:
(namespace agent
:export [cover identify])
;; --------------------------------------------------
;; Open another namespace:
(namespace guards)
;; You can call an exported function directly...
(agent/identify)
;; ...or import that namespace and call it with `_`:
(namespace guards :import [agent])
(_ identify)
; => "Hello, I'm David Somerset"
;; Public vars are available only through accessor functions.
(@ agent/cover)
; => ERROR
(agent/cover)
; => "David Somerset"
;; Private symbols are kept private:
(@ agent/real-name)
; => ERROR
(agent/real-name)
; => ERROR
The best way is to download and install the namespaces
package from
melpa.
- Make sure you've configured package management in your init.el:
```lisp
(require 'package)
;; Initialize packages.
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/"))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
```
- Install the
namespaces
package:
```lisp
;; Initialize namespaces.
(unless (package-installed-p 'namespaces)
(package-install 'namespaces))
(require 'namespaces)
```
Otherwise, clone this repo and add it to your load path, then:
(require 'namespaces)
The namespace
macro makes it easy to load dependencies using the
package management features in Emacs 24+; make sure you set up all that
package management stuff before you call those features.
You can define a namespace or reopen an existing one using the
namespace
macro:
(namespace enterprise)
You can define namespaced functions using defn
, and apply them using
_
.
(defn greet (name)
(concat "Hello, " name "!"))
(_ greet "Spock") ; => "Hello, Spock!"
Namespaced vars are defined using def
. You can retrieve their values
using the @
macro.
(def captain "Kirk")
(@ captain) ; => "Kirk"
Vars defined using def
are immutable. If you need a var that can
change, use defmutable
instead. You can change the value of a mutable
var using @set
.
(defmutable captain "Kirk")
(@set captain "Picard")
Symbols defined with defn
, def
and defmutable
are private unless
you explicitly export them. Vars defined with defmutable
cannot be
changed with @set
outside the defining namespace.
To make a namespaced symbol publicly-accessible, add it to the exports list for that namespace:
(namespace enterprise :export [ captain ])
This makes enterprise/captain
a public var, and generates an accessor
function. Other namespaces can now access that symbol by invoking the
accessor, or by adding it to their namespace imports and calling using
_
:
(namespace federation)
(enterprise/captain) ; => "Picard"
(namespace borg :import [ enterprise ])
(_ captain) ; => "Picard"
These accessor functions add a nice layer of safety: if your requirements change down the track (eg, you need logging), you can safely reimplement an acccessor by hand without changing the interface and breaking client code.
Clients of your namespaced code do not need to know anything about namespaces or the macros defined in this package.
The namespace
macro declares the given namespace as an Emacs feature,
as if you called (provide 'foo)
. Clients can use the standard
require
and autoload
mechanisms to access your exported functions.
Exported functions can be called using their fully qualified name:
(namespace foo :export [greet])
(defn greet () "Hello!")
(namespace bar)
(foo/greet) ; => "Hello!"
Similarly, exported vars can be read using their accessor functions:
(namespace foo :export [x])
(def x 'hello)
(namespace bar)
(foo/x) ; => hello
By design, clients cannot modify exported vars with @set
, even if they
are defined with defmutable
. Package writers should probably use
defcustom
when they want to define a var that can be customized by
clients.
The defn
, def
and defmutable
macros mangle their true symbols to
ensure namespaced identifiers are unique. You can obtain the underlying
symbol using the ~
macro. This is allows your private members to
interoperate with foreign elisp. For example:
(defn private () (message "TOP SECRET"))
(defvar example-hook)
(add-hook 'example-hook (~ private))
(run-hooks 'example-hook) ; check your *Messages* buffer
You can use the lambda-
macro when you want to capture the declaring
namespace in your hooks or exported functions:
(namespace foo :export [ x ])
;; Capture a private var in a closure.
(def private 'foo-private)
(def x (lambda- () (@ private)))
(namespace bar :import [ foo ])
(funcall (@ x)) ; => foo-private
It is sometimes necessary to enter a namespace manually within non-
namespaced code. The in-ns
macro is provided for this purpose.
(namespace foo)
(def x "inside foo")
(defun non-namespaced-fn ()
(in-ns foo
(message (@ x)))) ; => "inside foo"
The in-ns
macro provides a controlled means of breaking encapsulation.
When all else fails, you can use it to access private vars or redefine a
def
as defmutable
in code you do not control.
(namespace closed)
(def x "immutable")
(namespace user)
(in-ns closed
(defmutable x)
(@set x "now mutable!"))
namespace
takes a number of keyword arguments, which expect vectors as
their values.
Make the given functions or variables externally-accessible ('public').
(namespace foo :export [ x y z ... ])
Import public symbols from another namespace:
(namespace foo :export [x])
(def x "Hello")
(namespace bar :import [foo])
(_ x) ; => "Hello"
The default behaviour is to import all public symbols. You can load a subset by providing a list of symbols instead:
(namespace baz :import [ (foo x y) ])
The example above will import only x
and y
from namespace foo
.
Require an emacs feature.
(namespace foo :use [cl])
Lists are interpreted as autoload directives, where the first item is a feature name and the remainder are functions to be autoloaded.
(namespace foo :use [ (paredit paredit-mode) ])
Download the specified elisp packages.
You can eagerly require
the pacakge...
(namespace foo :packages [ auto-complete ])
...or autoload a list of symbols:
(namespace foo :packages [ (auto-complete auto-complete-mode) ])
The above forms take optional :when
or :unless
keyword arguments to
specify loading conditions. This can be useful for environment-specific
configuration.
(namespace foo :use [ (color-theme :when (display-graphic-p)) ])
Byte-compilation sometimes fails to expand definition forms. If you
experience issues, wrap everything after your namespace declaration in
an (@eval-after-load my-ns ...)
.
Due to internal name-mangling, you won't be able to use features like
eldoc or describe-function
on symbols defined by defn
, def
and
defmutable
. However, their fully-qualified exported forms work fine.
Elisp doesn't have reader macros, so there's not as much sugar as I'd like.