Commits (5)
(defsystem "alexandria"
:version "0.0.0"
:version "1.0.0"
:licence "Public Domain / 0-clause MIT"
:description "Alexandria is a collection of portable public domain utilities."
:author "Nikodemus Siivola <nikodemus@sb-studio.net>, and others."
:author "Nikodemus Siivola and others."
"Alexandria is a project and a library.
As a project Alexandria's goal is to reduce duplication of effort and improve
portability of Common Lisp code according to its own idiosyncratic and rather
conservative aesthetic. What this actually means is open to debate, but each
project member has a veto on all project activities, so a degree of
conservativism is inevitable.
conservative aesthetic.
As a library Alexandria is one of the means by which the project strives for
its goals.
......@@ -28,6 +26,8 @@ the following constraints:
* Conservative: Alexandria limits itself to what project members consider
conservative utilities. Alexandria does not and will not include anaphoric
constructs, loop-like binding macros, etc.
Also, its exported symbols are being imported by many other packages
already, so each new export carries the danger of causing conflicts.
* Portable: Alexandria limits itself to portable parts of Common Lisp. Even
apparently conservative and useful functions remain outside the scope of
(in-package :alexandria)
(defmacro with-gensyms (names &body forms)
"Binds each variable named by a symbol in NAMES to a unique symbol around
FORMS. Each of NAMES must either be either a symbol, or of the form:
"Binds a set of variables to gensyms and evaluates the implicit progn FORMS.
(symbol string-designator)
Each element within NAMES is either a symbol SYMBOL or a pair (SYMBOL
STRING-DESIGNATOR). Bare symbols are equivalent to the pair (SYMBOL SYMBOL).
Bare symbols appearing in NAMES are equivalent to:
(symbol symbol)
The string-designator is used as the argument to GENSYM when constructing the
unique symbol the named variable will be bound to."
Each pair (SYMBOL STRING-DESIGNATOR) specifies that the variable named by SYMBOL
should be bound to a symbol constructed using GENSYM with the string designated
by STRING-DESIGNATOR being its first argument."
`(let ,(mapcar (lambda (name)
(multiple-value-bind (symbol string)
(etypecase name
......@@ -28,23 +25,82 @@ unique symbol the named variable will be bound to."
`(with-gensyms ,names ,@forms))
(defmacro once-only (specs &body forms)
"Evaluates FORMS with symbols specified in SPECS rebound to temporary
variables, ensuring that each initform is evaluated only once.
"Constructs code whose primary goal is to help automate the handling of
multiple evaluation within macros. Multiple evaluation is handled by introducing
intermediate variables, in order to reuse the result of an expression.
The returned value is a list of the form
(let ((<gensym-1> <expr-1>)
(<gensym-n> <expr-n>))
where GENSYM-1, ..., GENSYM-N are the intermediate variables introduced in order
to evaluate EXPR-1, ..., EXPR-N once, only. RES is code that is the result of
evaluating the implicit progn FORMS within a special context determined by
SPECS. RES should make use of (reference) the intermediate variables.
Each element within SPECS is either a symbol SYMBOL or a pair (SYMBOL INITFORM).
Bare symbols are equivalent to the pair (SYMBOL SYMBOL).
Each of SPECS must either be a symbol naming the variable to be rebound, or of
the form:
Each pair (SYMBOL INITFORM) specifies a single intermediate variable:
(symbol initform)
- INITFORM is an expression evaluated to produce EXPR-i
Bare symbols in SPECS are equivalent to
- SYMBOL is the name of the variable that will be bound around FORMS to the
corresponding gensym GENSYM-i, in order for FORMS to generate RES that
references the intermediate variable
(symbol symbol)
The evaluation of INITFORMs and binding of SYMBOLs resembles LET. INITFORMs of
all the pairs are evaluated before binding SYMBOLs and evaluating FORMS.
(defmacro cons1 (x) (once-only (x) `(cons ,x ,x)))
(let ((y 0)) (cons1 (incf y))) => (1 . 1)
The following expression
(let ((x '(incf y)))
(once-only (x)
`(cons ,x ,x)))
;;; =>
;;; (let ((#1=#:X123 (incf y)))
;;; (cons #1# #1#))
could be used within a macro to avoid multiple evaluation like so
(defmacro cons1 (x)
(once-only (x)
`(cons ,x ,x)))
(let ((y 0))
(cons1 (incf y)))
;;; => (1 . 1)
The following expression demonstrates the usage of the INITFORM field
(let ((expr '(incf y)))
(once-only ((var `(1+ ,expr)))
`(list ',expr ,var ,var)))
;;; =>
;;; (let ((#1=#:VAR123 (1+ (incf y))))
;;; (list '(incf y) #1# #1))
which could be used like so
(defmacro print-succ-twice (expr)
(once-only ((var `(1+ ,expr)))
`(format t \"Expr: ~s, Once: ~s, Twice: ~s~%\" ',expr ,var ,var)))
(let ((y 10))
(print-succ-twice (incf y)))
;;; >>
;;; Expr: (INCF Y), Once: 12, Twice: 12"
(let ((gensyms (make-gensym-list (length specs) "ONCE-ONLY"))
(names-and-forms (mapcar (lambda (spec)
(etypecase spec
(defpackage :alexandria.0.dev
(defpackage :alexandria.1.0.0
(:nicknames :alexandria)
(:use :cl)