lisp-namespace
2022-11-07
Provides LISP-N --- extensible namespaces in Common Lisp.
Long time ago, in a galaxy far far away...
It is a period of a civil war. Lisp-2
aliens, striking from the function
namespace, have fought for the
design of their language against lisp-1.
tl;dr; → skip to API definition https://github.com/guicho271828/lisp-namespace#macro-define-namespace
- update (2016/5/14) : added support for `documentation` and `describe`.
- update (2016/5/21) : works on ECL, CLISP, ABCL and CMUCL.
1Introduction
There are several libraries which extends let. To my knowledge,
most of them goes toward destructuring, e.g., allowing
&some-fancy-directive in the argument list. However, destructuring is
now *obsolete*: superseded by pattern-matching (e.g. fare-matcher, optima, trivia), which
cleanly combines cond, typecase, destructuring and constructors[1].
Now it comes to my mind: what could be the orthogonal aspect
that could be combined with let, or, let + cond = pattern matching ?
Then I noticed an oddness in flet, macrolet, labels, let, symbol-macrolet. Pattern matching works in the value namespace only.
--- it's worth a library. This iswhat this library is for.[1] Like (cons A B) matching clause vs (cons A B) constructor.
2Namespaces in CL?
By namespace I don't mean a package, which is a way to manage symbols. It is orthogonal to namespace.
CL already has major 2 namespaces, function namespace and value namespace (or variable namespace), but there are actually more --- e.g., class namespace. Since the same symbol can represent different objects in each namespace, this is obviously orthogonal to package. For example, all these below can coexist in a same file without errors, and each object is accessible with the corresponding function.
(in-package :cl-user)
(defclass foo () ())
(defun foo () nil)
(defvar foo 1)
(find-class 'foo) ; -> #<STANDARD-CLASS FOO>
(symbol-function 'foo) ; -> #<FUNCTION FOO>
(symbol-value 'foo) ; -> 1
(make-instance 'bar) ; -> SIMPLE-ERROR
(symbol-function 'bar) ; -> UNDEFINED-FUNCTION
(symbol-value 'bar) ; -> UNBOUND-VARIABLE
| namespace | accessor | unbound condition | boundp | binding |
|---|---|---|---|---|
| class | find-class | SIMPLE-ERROR | n/a | n/a |
| function | symbol-function | UNDEFINED-FUNCTION | fboundp | flet,labels |
| value | symbol-value | UNBOUND-VARIABLE | boundp | let |
Some namespaces in CL can be said to overlap with each other. For example:
- class namespace --- with type, condition and struct namespace
- function namspace --- with macro-function and generic-function namespace
- value namespace --- with symbol-macro namespace.
2.1Macro DEFINE-NAMESPACE
(define-namespace name &optional (expected-type t) (binding t) (documentation ""))
This macro defines a namespace. For the given name of namespace X, DEFINE-NAMESPACE defines 4 functions/macros:
#'symbol-x, #'(setf symbol-x): accessor to the global binding. Optionally,expected-typeprovidesftypeproclamation and results in thebetter optimization.expected-typeis not evaluated.#'x-boundp: unary function returning a boolean- condition
UNBOUND-Xwhich is signaled when trying to access the value of an unbounded symbol. - macro
(X-LET (binding...) body): lexical binding. Can be turned offwhenbindingis nil.
2.2Extending the usability of documentation and describe
define-namespace also defines a method for cl:documentation and extend cl:describe-object. For example, you will be able to (setf (documentation 'mysymbol 'x) "description") and you will see it pretty printed in (describe 'mysymbol).
Here is an example used in TRIVIA pattern matcher. Trivia has an assoc pattern, and everyone would feel happy if we can browse the documentation of this pattern from SLIME C-c C-d. Below is such an output on SBCL.
While such a practice is taken by some libraries (e.g. QL-HTTP and Stefil defines describe methods), those facility is made independently, and this feature is not much popular.
COMMON-LISP:ASSOC
[symbol]
ASSOC names a compiled function:
Lambda-list: (ITEM ALIST &KEY KEY (TEST NIL TESTP)
(TEST-NOT NIL NOTP))
Declared type: (FUNCTION
(T LIST &KEY (:KEY (OR FUNCTION SYMBOL))
(:TEST (OR FUNCTION SYMBOL))
(:TEST-NOT (OR FUNCTION SYMBOL)))
(VALUES LIST &OPTIONAL))
Documentation:
Return the cons in ALIST whose car is equal (by a given test or EQL) to
the ITEM.
Known attributes: call, foldable, flushable, unsafely-flushable
Source file: SYS:SRC;CODE;LIST.LISP
+Symbol ASSOC is bound in a namespace PATTERN:
+ Value: #<FUNCTION 'ASSOC {1004B19A0B}>
+ Documentation:
+ It matches when the object X is a list, and then further matches the contents
+ returned by (cdr (assoc item X...)) against SUBPATTERN.
+ If :KEY and :TEST is specified, they are passed to ASSOC.
Note that namespace itself has its own namespace. The optional argument documentation to define-namespace is a docstring of the namespace itself. It will be set to (setf (documentation NAME 'namespace) documentation) and will also be visible from describe.
Examples are in EXAMPLE.org .
3Expected Usecase?
Every time you want to define a define-cool-object macro. E.g.,
- in eazy-project, defmenu
- in function-cache, defcached (currently implemented with hash tables)
- in optima, defpattern and pattern-expand-function (currently implementedwith symbol properties)
- in compiler-macro, define-compiler-hinter (currently implemented with hash tables)
- in cffi, define-foreign-library (currently implemented with hash tables)
4Other misc
4.1Macro NAMESPACE-LET / NSLET
LET with ability to lexically bind any value in the namespace.
It currently supports function, labels, value, symbol-macro, macrolet,
restart, handler [2] namespaces and the user-defined namespaces.
Full examples are in EXAMPLE.org .
(namespace-let ((#'x (y) (1+ y))
((macro x) (y) (1+ y))
((macro y) (y) (1+ y))
(#'x (y) (1+ y))
((label y) (y) (y y))
((symbol-macro sm) 0)
(b 0))
(let ((b 1))
(print :x)))
;; (PROGN
;; (FLET ((X (Y) (1+ Y)))
;; (MACROLET ((X (Y) (1+ Y))
;; (Y (Y) (1+ Y))) ; same kinds of bindings are merged
;; (FLET ((X (Y) (1+ Y)))
;; (LABELS ((Y (Y) (Y Y)))
;; (SYMBOL-MACROLET ((SM 0))
;; (LET ((B 0))
;; (PROGN
;; (LET ((B 1))
;; (PRINT :X))))))))))
[2] restarts and handlers have the dynamic scope only.
4.2Package LISP-NAMESPACE
it has (:nicknames lispn) .
5Design?
I'm wondering which abbreviation to namespace-let is appropriate.
It should be something consistent with the historic name as let.
However, I do not like names like let+ because they are not
self-expressive --- let+ does not describe how it's different from the
original let. bind and where are not considered good either, due to the
similar reason.
I adopted nslet, thanks to masatoi0@twitter's
advice. However, there is another alternative: Make it let and force the
user to shadow cl:let? (nah I don't like it.) I'm still searching for a
crazy bright idea.
Here are the remaining TODOs:
- X-let does not recognize
(declare (special ...))currently.
6Dependencies
This library is at least tested on implementation listed below:
- SBCL 1.2.8 on X86 Linux 3.13.0-44-generic (author's environment)
- CCL 1.10-r16196 (LinuxX8664)
Also, it depends on the following libraries:
- alexandria by ** :Alexandria is a collection of portable public domain utilities.
7Author & Copyright
Copyright (c) 2015 Masataro Asai (guicho2.71828@gmail.com)
Licensed under the LLGPL License.