Macros, functions, some things from Let Over Lambda
Common Lisp
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
COPYING
README
bang-symbols.lisp
do-mapper.lisp
html.lisp
list.lisp
locked-deque.lisp
macros.lisp
package.lisp
parse-body.lisp
rwlock.lisp
strings.lisp
symbols.lisp
utils-frahm-common.asd
utils-frahm-threaded.asd
utils-frahm.lisp

README

License

Release under a Simplified BSD License, see COPYING. Some parts from Let Over Lambda are used and their copyright notice is included.

Overview

A utility package with functions and macros thrown together with different purposes etc. Apart from some things of my own, tools from the book Let Over Lambda are included; eventually being rewritten. Currently only the form DEFMACRO! is available but more will follow.

ASDF definitions are available with the systems UTILS-FRAHM-COMMON and UTILS-FRAHM-THREADED (which is only tested with the locked queue implementation). The package is UTILS-FRAHM and depends on ANAPHORA.

My Own Tools

This is a collection of some more or less useful tools in general programming and macro writing.

Regarding macro programming I tried to handle border cases like empty expansions nicely so the resulting macroexpansion will probably look a bit closer to the hand-written equivalent (less empty LETs and other unused clutter, that is).

DEFVAR*, DEFCONSTANT*

DEFVAR* behaves like DEFVAR to create a new uninitialised variable with a documentation string; DEFCONSTANT* is DEFCONSTANT, except it behaves sane if a constant of that name is already bound.

DO-MAPPER, DO-MAPCAR, …

DO-MAPCAR adapts the MAPCAR function to accept a function body instead of a LAMBDA form. It accepts multiple lists (as does MAPCAR) and binds each element of each list to a user defined symbol or to the argument symbol. A user defined symbol is defined via syntax (SYMBOL LIST), therefore a quoted list may not be used (because of the actually present syntax (QUOTE (1 2 3))).

* (let ((list '(1 2 3)))
    (do-mapcar (list (another '(4 5 6)))
      (cons list another)))
=> ((1 . 4) (2 . 5) (3 . 6))

New macros of this kind may be defined in a similar way using an expansion of DO-MAPPER while providing a mapping function designator. For example the following use of DO-MAPPER simply prints all arguments to the mapping function:

* (do-mapper (lambda (&rest args) (format T "~S~%" args))
      ((list '(1 2 3)))
    list)
(#<FUNCTION (LAMBDA #) {1003908DB9}> (1 2 3))
=> NIL

DEFINE-MAPCAR/VALUES-N

This macro is rather big and currently defines a bunch of functions to run MAPCAR while accumulating multiple return values. The main defined function MAPCAR/VALUES accumulates as many return values as lists where supplied, MAPCAR/VALUES-N lets you specify the number of accumulated return values. There is no general case to accumulate an unspecified number of values, since this would imply rather costly list operations which I didn't want to include here.

It remains open to testing if this construct is useful enough to justify using it instead of expanding to cl-iterate, but it was a good exercise in macro writing nevertheless.

EQCASE, EQCOND

This macros are a generalisation of CASE to accept a function designator and using that to compare the cases with a keyform.

* (eqcase (2 :test #'=)
    (2 T)
    (T NIL))
=> T
* (eqcase ("foo" :test #'string-equal)
    (("foo" "bar") 'foobar)
    (T 'actual-t)
    (T 'unknown))
=> FOOBAR

The semantics match those of CASE. Also, they expand to COND and try to remove some unnecessary forms.

EQCOND just doesn't quote its keys, so something like the following works:

* (let ((foo 42))
    (eqcond (42 :test #'=)
      (foo T)))
=> T

Both macros accept the parameter ERROR-P, which can be either T, NIL, or one of :ERROR and :CERROR.

Let Over Lambda tools

These are various tools from the book Let Over Lambda by Doug Hoyte, slightly edited to correctly handle some minor aspects, for example docstrings and declarations and enhanced for better readability of the generated code. For the original source code go to the Let Over Lambda homepage.

ALAMBDA

This macro binds the created function to the anaphoric IT inside the body.

* (funcall (alambda (x) (if (null x) 42 (it (cdr x)))) '(1 2 3))
=> 42

Since it's not a LAMBDA form, it can't be used in function position though.

DEFMACRO!

DEFMACRO! provides two nice additions to macro writers: generating gensyms and ensuring once-only evaluation using a special and probably unused symbol naming convention, namely G!FOO for a new gensym and O!FOO for automatic once-only evaluation of the macro argument of the stripped name. It is used to remove unnessecary clutter from macros while giving each function a distinct visual look.

* (defmacro! foo (o!x y)
    `(list ',g!x ,g!x ,x ,y))
* (foo (list 1 2 3) 42)
=> (#:X1789 (1 2 3) (1 2 3) 42)

The expansion of FOO probably gives a better idea what happens here:

(LET ((#:X1790 (LIST 1 2 3)))
  (DECLARE (IGNORABLE #:X1790))
  (LIST '#:X1790 #:X1790 (LIST 1 2 3) 42))

For every evaluated argument via O!FOO every use of a G!FOO symbol in the body of the macro refers to a shared LET-bound variable. Since the argument list of the macro is rewritten, the use of a O!FOO symbol in the body is an error and won't work. As visible above the use of the actual argument (here X) isn't forbidden and refers to the unedited macro argument.

Additionally, the wrapped body of the macro is contained in a named block like the name of the defined macro. If necessary, an additional block with the prefix OUTER- prior to the original macro name is established if you really want to exit the confinement of the extra constructs.

If available, the second returned value is an association list, which may toggle the usage of the LET-bindings if they're not needed (via :LET-P), the other disables the (DECLARE (IGNORABLE)) declarations for LET-bound variables (via :IGNORABLE-P), more a stylistic addition if the variables are guaranteed to be used every time.