positional-lambda
2012-10-13
positional-lambda is a concise, intuitive and flexible syntax (macro) for trivial lambdas that eschews explicit (and often contextually-redundant) naming of parameter variables in favor of positional references, with support for a used or ignored &rest parameter and automatic declaration of ignored parameters when logical "gaps" are left in the positional references. Further convenience features are provided.
Author
Jean-Philippe Paradis <hexstream@gmail.com>
License
Public Domain
Project's home: http://www.hexstreamsoft.com/projects/positional-lambda/
(This documentation is pretty bare, I'll update it when I'm done
making a great documentation system. I didn't yet take care of the
"paucity of examples" and "absence of formal description" problems.)
positional-lambda is a concise, intuitive and flexible syntax (macro)
for trivial lambdas that eschews explicit (and often
contextually-redundant) naming of parameter variables in favor of
positional references, with support for a used or ignored &rest
parameter and automatic declaration of ignored parameters when logical
"gaps" are left in the positional references. Further convenience
features are provided.
(plambda (values :3 :1))
==
(lambda (first second third)
(declare (ignore second))
(values third first))
(plambda (list* :2 :1 :rest))
==
(lambda (first second &rest rest)
(list* second first rest))
(plambda :rest (list :2 :1))
==
(lambda (first second &rest rest)
(declare (ignore rest))
(list second first))
It's possible to specify a minimum number of required arguments:
(plambda :3 :2)
==
(lambda (first second third)
(declare (ignore first third))
second)
(plambda :2 :3) ; redundant minimum: 3 > 2.
==
(plambda :3)
Which also has the effect of "pushing back" the &rest argument if there's one:
(plambda :3 (mapcar :1 :rest))
==
(lambda (first second third &rest rest)
(declare (ignore second third))
(mapcar first rest))
The first argument to PLAMBDA is treated as a specification of the
minimum number of arguments only if it looks like a positional reference
and PLAMBDA was invoked with other arguments:
(plambda :2)
==
(lambda (first second)
(declare (ignore first))
second)
PLAMBDA accepts an implicit PROGN, not just one expression:
(plambda (print :1) :2)
==
(lambda (first second)
(print first)
second)
Also, PLAMBDA's :let "local special operator" allows one to "lift" parts of
its body outside the lambda to a LET without needing to name and then
refer to an explicit variable.
(plambda :2 (list :1 (:let (random))))
==
(let ((number (random)))
(lambda (first second)
(declare (ignore second))
(list first number)))
Another feature is :cache, which is like :let except it computes the
associated form in its original lexical and dynamic context within the
lambda the first time its evaluation completes and returns the cached
value on subsequent evaluations.
(plambda (write :1 :base (:cache *print-base*)))
==
(let (base basep)
(lambda (first)
(write first :base (if basep
base
(prog1 (setf base *print-base*)
(setf basep t))))))
The consequences are undefined if the :let and :once local special
operators are nested within themselves or eachother:
(plambda (:let (:let 1))) ; undefined
PLAMBDA will treat any quoted expressions as opaque, and will treat
anything wrapped in the :quote local-special-form as opaque as well.
(plambda ':1)
==
(lambda () ':1)
(plambda () (:quote :1))
==
(lambda () :1)
Unfortunately, currently PLAMBDA won't do the right thing with some
expressions that are quoted via backquote (since there's no easy
portable way to walk backquoted expressions).
(plambda `(:1 ,:2))
==
[:1 will be erroneously replaced]
To use lambda-lift, simply (:import-from #:lambda-lift #:lift) from
your DEFPACKAGE. Don't (:use #:lambda-lift)! "Clashy" symbols might be
added to the lambda-lift package in the future.
positional-lambda should only be used to describe actually trivial
lambdas, usually no more than 3 lines long, and should not be used in
code returned by macros, because of "capture" issues. In particular,
due to the PLAMBDA macro's parsing model, it's not safe to nest multiple
invocations.
However, these are not practical limitations since as PLAMBDA's
expressions get bigger, the benefits of conciseness quickly fade out
to be replaced by the problems of implicitness, so hopefully one
wouldn't be tempted to use it in these scenarios anyway. The
implicitness is not a problem for suitably short PLAMBDA expressions
since the surrounding context provides enough contextual information,
and explicitly naming variables would then simply provide redundant
information.
PLAMBDA's deliberately simplistic "surface parsing" (and replacement)
strategy is conceptually and implementationally simple and robust, as
opposed to often-brittle "code-walking" where an attempt is made to
take into account the actual semantics of forms and operators, often
necessitating explicit support when new operators are introduced.
This library is in the Public Domain.
See the UNLICENSE file for details.