Git Product home page Git Product logo

lisp-namespace's Introduction

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

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

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
namespaceaccessorunbound conditionboundpbinding
classfind-classSIMPLE-ERRORn/an/a
functionsymbol-functionUNDEFINED-FUNCTIONfboundpflet,labels
valuesymbol-valueUNBOUND-VARIABLEboundplet

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

[2] restarts and handlers have the dynamic scope only.

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 ([email protected])

Licensed under the LLGPL License.

lisp-namespace's People

Contributors

guicho271828 avatar kilianmh avatar macdavid313 avatar mpsota avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

lisp-namespace's Issues

The type declaration here is missing the package information

guicho271828/trivia#12

(trivia.level2.impl::pattern-let ((vec1 (lambda (x) `(vector ,x))))
     (pattern-expand-all arg))

expands into,

(PROGN
 (LET ((#:TEMP835 (LAMBDA (X) `(VECTOR ,X))))
   (DECLARE (TYPE (PATTERN-TYPE) #:TEMP835))
   (MACROLET ((SYMBOL-PATTERN (&WHOLE LISP-NAMESPACE::WHOLE LISP-NAMESPACE::X)
                (IF (EQUAL LISP-NAMESPACE::X ''VEC1)
                    '#:TEMP835
                    LISP-NAMESPACE::WHOLE)))
     (PROGN (PATTERN-EXPAND-ALL ARG)))))

The type declaration here is missing the package information for some reason.

USE-VALUE does not work via repl.

Here is the session.

* (lispn:define-namespace test)

""
* (symbol-test 'no)

debugger invoked on a UNBOUND-TEST in thread
#<THREAD "main thread" RUNNING {10014F0073}>:
  Symbol NO is unbound in namespace TEST

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
  0: [USE-VALUE] USE-VALUE
  1: [ABORT    ] Exit debugger, returning to top level.

(SYMBOL-TEST NO NIL)
  source: (RESTART-CASE (ERROR 'UNBOUND-TEST :NAME SYMBOL)
            (USE-VALUE (LISP-NAMESPACE::DEFAULT)
              (SETF (SYMBOL-TEST SYMBOL) LISP-NAMESPACE::DEFAULT)))
0] 0

debugger invoked on a SB-INT:SIMPLE-PROGRAM-ERROR @5378BF11 in thread
#<THREAD "main thread" RUNNING {10014F0073}>:
  invalid number of arguments: 0

;;;; OR

(handler-bind ((unbound-test
                 (lambda (c)
                   (let ((restart (find-restart 'use-value c)))
                     (when restart
                       (invoke-restart-interactively restart))))))
  (symbol-test 'no))

It seems that use-value clause missing :interactive option.

https://github.com/guicho271828/lisp-namespace/blob/master/src/package.lisp#L116-L117

http://www.lispworks.com/documentation/HyperSpec/Body/m_rst_ca.htm

Method on DESCRIBE-OBJECT :AFTER on SYMBOL invokes UB

(defmethod describe-object :after ((x symbol) s)

As useful as this method is, this isn't portable code - it violates CLHS 11.1.2.1.2.19 and therefore invokes undefined behavior:

Except where explicitly allowed, the consequences are undefined if any of the following actions are performed on an external symbol of the COMMON-LISP package:
...
19. Defining a method for a standardized generic function which is applicable when all of the arguments are direct instances of standardized classes.

We'll need to look at something implementation-defined in order to get something that's not effectively UB.

Handle CLISP and LispWorks properly

There are workarounds for these two implementations:

#+clisp
(format *error-output* "On CLISP, we cannot add method to DESCRIBE-OBJECT, so you cannot enjoy extended documentations for various namespaces")
#+lispworks
(format *error-output* "On Lispworks, we cannot add method to DESCRIBE-OBJECT, so you cannot enjoy extended documentations for various namespaces")

It does not seem that either of these implementations supply their own :AFTER method for DESCRIBE-OBJECT SYMBOL T; they only have package locking. In particular:

CLISP 2.49.93+

[1]> (ext:without-package-lock ("COMMON-LISP") (defmethod describe-object :after ((o symbol) stream) (print "hahaha" stream)))
#<STANDARD-METHOD :AFTER (#<BUILT-IN-CLASS SYMBOL> #<BUILT-IN-CLASS T>)>
[2]> (describe 'foo)

FOO is the symbol FOO, lies in #<PACKAGE COMMON-LISP-USER>, is accessible in 1 package COMMON-LISP-USER.

 #<PACKAGE COMMON-LISP-USER> is the package named COMMON-LISP-USER. It has 2 nicknames CL-USER, USER.
 It imports the external symbols of 2 packages COMMON-LISP, EXT and exports no symbols, but no package uses these
 exports.

"hahaha"

LispWorks 7.1.2 Personal

CL-USER 4 > (let ((lispworks:*handle-warn-on-redefinition* nil)) (defmethod describe-object :after ((o symbol) stream) (print "hahaha" stream)))
#<STANDARD-METHOD DESCRIBE-OBJECT (:AFTER) (SYMBOL T) 4020158D5B>

CL-USER 5 > (describe 'foo)

FOO is a SYMBOL
NAME          "FOO"
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       #<The COMMON-LISP-USER package, 5/16 internal, 0/4 external>
"hahaha" 

Backport changes from In Nomine

https://github.com/phoe/in-nomine is now out and good for public consumption; it's a backwards-incompatible fork of lisp-namespace that e.g. removes namespace-let and moves hashtables from implicitly created dynamic variables into namespace objects. It also handles #13 much better and solves #14.

Other than for removing nslet, the short form of in-nomine:define-namespace maintains syntax compatibility with lisp-namespace, hence maybe there's a chance for stealing some code and tests back into here.

It should be possible to add define-symbol-macros for imitating the generated variables; if not, then it should be possible to add them back without too much effort. Maybe it could even be possible to hack nslet back in.

Once you have a while to take a look at my code, please tell me what you think about the idea.

describe-object method cannot be defined in LispWorks 7

CL-USER 5 > (ql:quickload :lisp-namespace)
To load "lisp-namespace":
  Load 1 ASDF system:
    lisp-namespace
; Loading "lisp-namespace"

Error: Defining (METHOD DESCRIBE-OBJECT :AFTER (SYMBOL T)) visible from package COMMON-LISP.
   1 (continue) Define it anyway.
   2 Discard the new method.
   3 Try loading /Users/elangley/.cache/common-lisp/lw-7.0.0-macosx-x64/Users/elangley/quicklisp/dists/quicklisp/software/lisp-namespace-20160825-git/src/namespace.64xfasl again.
   4 Give up loading /Users/elangley/.cache/common-lisp/lw-7.0.0-macosx-x64/Users/elangley/quicklisp/dists/quicklisp/software/lisp-namespace-20160825-git/src/namespace.64xfasl.
   5 Try loading another file instead of /Users/elangley/.cache/common-lisp/lw-7.0.0-macosx-x64/Users/elangley/quicklisp/dists/quicklisp/software/lisp-namespace-20160825-git/src/namespace.64xfasl.
   6 Recompile namespace and try loading it again
   7 Retry loading FASL for
   #<ASDF/LISP-ACTION:CL-SOURCE-FILE "lisp-namespace" "src" "namespace">.
   8 Continue, treating loading FASL for
   #<ASDF/LISP-ACTION:CL-SOURCE-FILE "lisp-namespace" "src" "namespace">
     as having been successful.
   9 Retry ASDF operation.
  10 Retry ASDF operation after resetting the configuration.
  11 (abort) Give up on "lisp-namespace"
  12 Return to level 0.
  13 Restart top-level loop.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.