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
- 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
```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:
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.
Defining Namespaces and Members
You can define a namespace or reopen an existing one using the
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
(def captain "Kirk") (@ captain) ; => "Kirk"
Vars defined using
def are immutable. If you need a var that can
defmutable instead. You can change the value of a mutable
(defmutable captain "Kirk") (@set captain "Picard")
Symbols defined with
defmutable are private unless
you explicitly export them. Vars defined with
defmutable cannot be
@set outside the defining namespace.
Exporting and Importing Namespaced Symbols
To make a namespaced symbol publicly-accessible, add it to the exports list for that namespace:
(namespace enterprise :export [ captain ])
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.
namespace macro declares the given namespace as an Emacs feature,
as if you called
(provide 'foo). Clients can use the standard
autoload mechanisms to access your exported functions.
Functions and Vars
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
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"
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
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
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
y from namespace
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
:unless keyword arguments to
specify loading conditions. This can be useful for environment-specific
(namespace foo :use [ (color-theme :when (display-graphic-p)) ])
Gotchas and Limitations
Byte-compilation sometimes fails to expand definition forms. If you
experience issues, wrap everything after your namespace declaration in
(@eval-after-load my-ns ...).
Due to internal name-mangling, you won't be able to use features like
describe-function on symbols defined by
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.