lisp-namespace

2016-08-25

https://travis-ci.org/guicho271828/lisp-namespace.svg?branch=master

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.

Introduction

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 constructors1. 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 is what this library is for.

Namespaces 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.

Macro 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-type provides ftype proclamation and results in the better optimization. expected-type is not evaluated.
  • #'x-boundp : unary function returning a boolean
  • condition UNBOUND-X which is signaled when trying to access the value of an unbounded symbol.
  • macro (X-LET (binding...) body) : lexical binding. Can be turned off when binding is nil.

Extending 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 .

Expected Usecase?

Every time you want to define a define-cool-object macro. E.g.,

Other misc

Macro 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))))))))))

Package LISP-NAMESPACE

it has (:nicknames lispn) .

Design?

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.

Dependencies

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.

Author & Copyright

Copyright (c) 2015 Masataro Asai (guicho2.71828@gmail.com)

Licensed under the LLGPL License.


  1. Like (cons A B) matching clause vs (cons A B) constructor.

  2. restarts and handlers have the dynamic scope only.

Author
Masataro Asai
License
LLGPL