Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Macros, functions, some things from Let Over Lambda
Common Lisp
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

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

<!-- -*- mode: markdown; mode: auto-fill; fill-column: 72; -*- -->

# 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](http://letoverlambda.com/) 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 `LET`s 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`, &hellip;

`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](http://letoverlambda.com/) 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.
Something went wrong with that request. Please try again.