├── README.md ├── example.el └── fakespace.el /README.md: -------------------------------------------------------------------------------- 1 | Elisp Namespaces 2 | ================ 3 | 4 | This package provides fake namespaces for Emacs Lisp through a 5 | rudimentary version of `defpackage`. 6 | 7 | ```cl 8 | (defpackage example 9 | (:use cl ido) 10 | (:export example-main example-var)) 11 | ```` 12 | 13 | Symbols that are not exported are hidden from any access outside the 14 | package. See the header of `fakespace.el` for more information, and 15 | `example.el` for a full example of its use. 16 | 17 | See also: http://nullprogram.com/blog/2011/08/18/ 18 | -------------------------------------------------------------------------------- /example.el: -------------------------------------------------------------------------------- 1 | (require 'fakespace) 2 | 3 | ;; Start by declaring a package. `defpackage' currently supports :use 4 | ;; and :export. Any libraries listed in :use will be `require'd. You 5 | ;; can make your own calls to require, but they should generally occur 6 | ;; *before* your `defpackage'. Otherwise the symbols defined in the 7 | ;; require will become part of your package and won't get exported. 8 | 9 | (defpackage example 10 | (:use cl ido) 11 | (:export example-main example-var eq-hello hello)) 12 | 13 | ;; Caveat: any functions or variables you declare *will* be defined in 14 | ;; the main namespace (we're faking namespaces here), but the 15 | ;; non-exported symbols will be removed afterward. The same functions 16 | ;; and variables can be redefined elsewhere outside this package 17 | ;; without interfering with the definitions here. 18 | 19 | (defvar my-var 100 20 | "A hidden variable.") 21 | 22 | (defvar example-var nil 23 | "A public variable.") 24 | 25 | (defun my-func () 26 | "A private function." 27 | my-var) 28 | 29 | (defun example-main () 30 | "An exported function. Notice we can access all the private 31 | variables and functions from here." 32 | (interactive) 33 | (list (list (my-func) my-var) example-var 34 | (ido-completing-read "New value: " (list "foo" "bar")))) 35 | 36 | (defun eq-hello (sym) 37 | (eq sym 'hello)) 38 | 39 | ;; Unlike Common Lisp, rather than declaring your namespace with 40 | ;; `in-package' you must end your package definition with 41 | ;; `end-package'. This will hide all of your internal functions away 42 | ;; from the main namespace. 43 | 44 | (end-package) 45 | -------------------------------------------------------------------------------- /fakespace.el: -------------------------------------------------------------------------------- 1 | ;;; fakespace.el --- fake namespaces with defpackage 2 | 3 | ;; This is free and unencumbered software released into the public domain. 4 | 5 | ;; Author: Christopher Wellons 6 | ;; URL: https://github.com/skeeto/elisp-fakespace 7 | ;; Version: 1.0 8 | 9 | ;;; Commentary: 10 | 11 | ;; Provides fake namespaces through a very rudimentary `defpackage', 12 | ;; familiar to Common Lisp users. The developer declares which symbols 13 | ;; are to be exported outside the package. All other symbols are 14 | ;; hidden away (by `end-package') where no other package can trample 15 | ;; on them. 16 | 17 | ;; See example.el for an example of using this package. 18 | 19 | ;; It works by comparing the symbol table before any declarations to 20 | ;; the symbol table afterward, then uninterning any symbols that were 21 | ;; created and not exported. This will work even on byte-compiled 22 | ;; files. In fact, everything is determined at compile-time, so there 23 | ;; is practically no load-time penalty from using these fake 24 | ;; namespaces. 25 | 26 | ;; Generally you will not want to actively use these fake namespaces 27 | ;; during development because there is no `in-package' function. You 28 | ;; will always been in the main namespace unable to access your 29 | ;; private functions. Later, when you're finishing up and want to test 30 | ;; out your namespace, make sure your code is unloaded from Emacs 31 | ;; (i.e. none of your symbols is in the symbol table) before applying 32 | ;; your namespace. The same applies to compilation: the package must 33 | ;; not be loaded before compilation or the `defpackage' will not hide 34 | ;; any symbols. 35 | 36 | ;; Unfortunately, due to the downright ugly implementation of obarrays 37 | ;; in Emacs, it's currently not possible to implement 38 | ;; `in-package'. Uninterned symbols cannot be added back to the global 39 | ;; obarray (or any other obarray). Symbols are invisibly chained as 40 | ;; linked lists in the obarray so it's not possible to put a symbol 41 | ;; into two obarrays at the same time -- the chains would 42 | ;; conflict. Not only is the required functionality not provided 43 | ;; (intentionally), trying to hack it in would break everything. 44 | 45 | ;;; Code: 46 | 47 | (defun fakespace--atom-list (&optional ob) 48 | "Return given obarray OB as a list. Defaults to obarray." 49 | (let ((lst ())) 50 | (mapatoms (lambda (s) (push s lst)) ob) 51 | lst)) 52 | 53 | (defun fakespace--atom-difference (a b) 54 | "Like set-difference, but, for performance reasons, requires 55 | specially formed lists (i.e. from `fakespace--atom-list'). Returns 56 | items that are in B and not A." 57 | (let ((diff)) 58 | (while (and (not (null a)) (not (null b))) 59 | (while (not (eq (car a) (car b))) 60 | (push (car b) diff) 61 | (setq b (cdr b))) 62 | (setq a (cdr a)) 63 | (setq b (cdr b))) 64 | diff)) 65 | 66 | (defvar fakespace--obarray () 67 | "Snapshot of the obarray before interning the package's symbols.") 68 | 69 | (defmacro defpackage (name &rest args) 70 | (dolist (arg args) 71 | (let ((type (car arg))) 72 | (cond ((eq type :exports) t) ; interning the symbols is enough 73 | ((eq type :use) (mapc (lambda (s) (require s)) (cdr arg)))))) 74 | (setq fakespace--obarray (fakespace--atom-list) ) 75 | `(provide (quote ,name))) 76 | 77 | (defmacro end-package () 78 | (cons 'progn 79 | (mapcar (lambda (s) `(unintern (quote ,s) nil)) 80 | (fakespace--atom-difference fakespace--obarray 81 | (fakespace--atom-list))))) 82 | 83 | (provide 'fakespace) 84 | 85 | ;;; fakespace.el ends here 86 | --------------------------------------------------------------------------------