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.
Jean-Philippe Paradis <email@example.com>
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.