multiple-value-variants
2014-08-26
Gives access to multiple-value variants of operators through one macro: MULTIPLE-VALUE. There are built-in variants for some standard operators; it's easy to create your own variants for other operators. The multiple-value mapping operators are especially useful.
Author
Jean-Philippe Paradis <hexstream@gmail.com>
License
Public Domain
Project's home: http://www.hexstreamsoft.com/projects/multiple-value-variants/
multiple-value-variants gives access to multiple-value variants of
operators through one macro: MULTIPLE-VALUE. There are built-in
variants for some standard operators; it's easy to create your own
variants for other operators. The multiple-value mapping operators are
especially useful.
Examples
--------
(multiple-value () (progn 1 2 3))
=> 3
(multiple-value () (progn))
=> [no values]
(multiple-value ()
(and (values nil 'other 'values)
t))
=> NIL, OTHER, VALUES
(multiple-value ()
(or (find-symbol "OR" '#:cl)
(find-symbol "XOR" '#:cl)))
=> OR, :EXTERNAL
(let ((hash (make-hash-table)))
(multiple-value () (cond ((gethash 'key hash))
((values)))))
=> [no values]
(let ((hash (make-hash-table)))
(setf (gethash 'key hash) 'value)
(multiple-value () (cond ((gethash 'key hash))
((values)))))
=> VALUE, T
(multiple-value ()
(when nil
(print "side-effect")))
=> [no values]
(multiple-value (2)
(mapcar #'truncate '(3 5/4 5.5)))
=> (3 1 5), (0 1/4 0.5)
(multiple-value 2
(mapcan (lambda (object)
(if (numberp object)
(values (list object) nil)
(values nil (list object))))
'(0 a 2 3/4 c)))
=> (0 2 3/4), (A C)
(multiple-value 3
(maplist (lambda (tail)
(values tail
(reverse tail)
(list (first tail) (second tail))))
'(a b c d e)))
=>
((A B C D E) (B C D E) (C D E) (D E) (E))
((E D C B A) (E D C B) (E D C) (E D) (E))
((A B) (B C) (C D) (D E) (E NIL))
API
---
First of all, in the way of packages there's the
MULTIPLE-VALUE-VARIANTS package, which is also nicknamed
MULTIPLE-VALUE-VARIANT, MV-VARIANTS and MV-VARIANT. The primary
exported symbol is the MULTIPLE-VALUE macro. Explicitly (:import-from
#:multiple-value-variants #:multiple-value) for normal usage. Don't (:use)!
The MULTIPLE-VALUE-VARIANTS package also exports other symbols related
to creation of new multiple-value variants and querying of existing
ones (documentation pending, check package.lisp for a list of all
exported symbols). The most important of these is DEFINE, which is
normally used to define new multiple-value variants. You should
normally explicitly package-qualify this symbol.
There are 2 recurring features throughout the API:
IDENTITY generally indicates the form to evaluate (and values to
return) when an implicit (multiple-value progn) has no forms.
NTH indicates which value to test for conditionals.
The rest of the API documentation introduces the built-in
multiple-value variants.
Variant PROGN
(&key (identity '(values)))
(&body forms)
Just like PROGN, except if there are no FORMS then the IDENTITY is
evaluated instead.
Variant PROG1
()
(result &body body)
This straightforwardly expands to a MULTIPLE-VALUE-PROG1.
Variant AND
(&key identity (nth 0))
(&rest forms)
If IDENTITY is not specified, then there must be at least one FORM.
This is like normal AND, except if one of the non-last FORMS'
NTH-value is false, returns all values that were returned by that
FORM, not just the primary value.
Variant OR
(&key identity (nth 0))
(&rest forms)
If IDENTITY is not specified, then there must be at least one FORM.
This is like normal OR, except if one of the non-last FORMS'
NTH-value is true, returns all values that were returned by that
FORM, not just the primary value.
Variant COND
(&key (nth 0))
(&rest clauses)
This is just like normal COND, except:
- If a CLAUSE that has no FORMS succeeds, then all the values that
were returned by the TEST-FORM are returned, not just the primary
value;
- If no CLAUSE succeeds, then no values are returned (instead of NIL).
Variant WHEN
(&key (else '(values)) (identity '(values)))
(test &body forms)
If TEST evaluates to true, evaluate FORMS as an implicit
(multiple-value progn) with IDENTITY as the identity.
Else, evaluate ELSE.
Variant UNLESS
(&key (else '(values)) (identity '(values)))
(test &body forms)
If TEST evaluates to false, evaluate FORMS as an implicit
(multiple-value progn) with IDENTITY as the identity.
Else, evaluate ELSE.
Variant CASE () (keyform &body cases)
Variant CCASE () (keyplace &body cases)
Variant ECASE () (keyform &body cases)
Variant TYPECASE () (keyform &body cases)
Variant CTYPECASE () (keyplace &body cases)
Variant ETYPECASE () (keyform &body cases)
These are like their normal counterparts, except the FORMS in each
case is an implicit (multiple-value progn), and if no case matches in
CASE or TYPECASE, then no values are returned (instead of NIL).
Variant MAPCAR (multiple-values-count) (function &rest+ lists)
Variant MAPCAN (multiple-values-count) (function &rest+ lists)
Variant MAPLIST (multiple-values-count) (function &rest+ lists)
Variant MAPCON (multiple-values-count) (function &rest+ lists)
These are just like the normal variants, except they can accumulate
multiple results at the same time. This is especially useful to
"triage" values (ex: split the elements of a list into 2 lists
according to some criteria), and to accumulate multiple "layers" of
values in one pass for macroexpansions, as an alternative to repeated
mapping (sometimes with some readability problems due to reduced
"correlation").
MULTIPLE-VALUES-COUNT is not evaluated, and must be a non-negative
integer indicating the number of results (values) to accumulate and
return. FUNCTION would normally return that many values. If FUNCTION
returns less than MULTIPLE-VALUES-COUNT values, then the remaining
values are NIL. If FUNCTION returns more than MULTIPLE-VALUES-COUNT
values, then the excess values are ignored.
This library is in the Public Domain.
See the UNLICENSE file for details.