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.
⚓
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 mv-variant: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.
⚓
- ⚓
Variant progn (&key (identity '(values))) (&body forms)
-
Just like
progn
, except if there are no forms then the identity is evaluated instead.(
multiple-value
() (progn
1 2 3)) => 3(
multiple-value
() (progn
)) => [no values] - ⚓
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.(
multiple-value
() (and
(valuesnil
'other 'values) t)) =>NIL
, OTHER, VALUES - ⚓
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.(
multiple-value
() (or
(find-symbol
"OR"
'#:cl) (find-symbol
"XOR"
'#:cl))) => OR, :EXTERNAL - ⚓
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
).
(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 - ⚓
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.
(
multiple-value
() (when
nil
(print"side-effect"
))) => [no values] - ⚓
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 incase
ortypecase
, then no values are returned (instead ofnil
).- ⚓
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.(
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
) (valuesnil
(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))