The package essentials for Common Lisp provides 4 Macros which aid in formatting and readability of Common Lisp source code.
- License: GNU General Public License 3
- Portability: Standard conformant
- Tested implementations: CLisp, SBCL
- Article refers to version 0.0.1 of package
- Download package (current version)
- See also:
The all-values macro collects all return values of all immediatly contained forms and returns them as multiple values in order of program flow.
(all-values (floor 9 6) (floor 7 3))
returns four values: 1, 3, 2, 1.
In implementations of Common Lisp which do not store multiple return values on the stack, their use can have negative effect on performance. An example is CLisp which stores multiple return values as a list rather than on the stack.
Macro mlet, mlet*
The two macros mlet and mlet* fuse the syntax of let and multiple-value-bind on the one hand side, and of let* and multiple-value-bind on the other hand side.
The structure of an mlet form is similar to the structure of a let form. In particular, every valid let form can be turned into a valid mlet form by replacing the symbol let by the symbol mlet.
Other than let, mlet can bind multiple return values, and can thus replace the use of multiple-value-bind in program source.
Symbols additionaly needed to bind further return values are placed in the respective binding clauses between the symbol for the first return value and the initform of the binding clause.
mlet binds the values of the initforms in parallel just like let; mlet* binds the values in order of program code just like let*. In both cases, the binding of multiple return values from single initforms will be performed in parallel just like in a multiple-value-bind form.
(let ((x (random (expt 10 10)))) (multiple-value-bind (quotient remainder) (floor x) (format t "X: ~D · Quotient: ~D · Remainder: ~D" x quotient remainder)))
can be replaced by
(mlet* ((x (random (expt 10 10)))) ((quotient remainder (floor x))) (format t "X: ~D · Quotient: ~D · Remainder: ~D" x quotient remainder)))
Like the clauses of the iterate macro, binding clauses of mlet and mlet* can carry type declarations (green typeface in following example).
(mlet* (((the bignum x) (random (expt 10 10)))) (((the unsigned-byte quotient) (the unsigned-byte remainder) (floor x))) (declare (optimize (speed 2))) (format t "X: ~D · Quotient: ~D · Remainder: ~D" x quotient remainder)))
The expansion of mlet and mlet* places type declarations in such way that their scope matches with the scope of their respective bindings. In the preceeding example, the scope of the type declaration bignum thus encompasses the scope of the unsigned-byte type declaration. Like in let and let* forms, further declarations can be made after the binding clauses.
The if? macro is similar to the if* macro of John K. Foderaro. Like if*, if? provides a variant of if (or rather cond) structured by keywords, in order to increase readability of program source. Other than if*, the branches of the if? form are surrounded by brackets. This supports proper indentation in text editors.
The following forms are all equivalent:
;; 1950ies classic Lisp conditional <cond> (cond (testform-1) (testform-2 alpha-1 alpha-2 alpha-3) ('t beta-1 beta-2)) ;; Common Lisp <if> special form (let ((testvalue-10 testform-1)) (if testvalue-10 testvalue-10 (if testform-2 (progn alpha-1 alpha-2 alpha-3) (progn beta-1 beta-2)))) ;; John K. Foderaro's <if*> macro (if* testform-1 :thenret :elseif testform-2 :then alpha-1 alpha-2 alpha-3 :else beta-1 beta-2) ;; Typical indentation of <if?> macro (if? testform-1 (:thenret) (:elseif testform-2) (:then alpha-1 alpha-2 alpha-3) (:else beta-1 beta-2))
Even this simple example shows that the use of the if special form of the Common Lisp standard leads to absurdly obscure indentation.
In the if? macro, return values of the test forms can be bound to variable names. Their binding encompasses all following clauses of the respective if? form. This also holds for multiple return values of a test form. Resembling the syntax of the iterate macro, the values keyword is used for this purpose (blue typeface in following example). Only the first return value of an :if or :elseif clause ist testet to determine control flow.
(if? (values testvalue-10 testform-1) (:thenret) (:elseif (values testvalue-20 (ignore testvalue-21) (the ratio testvalue-22) testform-2) (:then alpha-1 alpha-2 ;; Here, the two format args are bound to ;; return values from the above testforms: (format t "~S and ~S" testvalue-10 testvalue-22) alpha-3 (:else beta-1 beta-2))
Here, control flow is determined by the variables testvalue-10 and testvalue-20. Type declarations can be made like in the macros mlet and mlet* (green typeface above); ignore and ignorable declarations are also possible, with the same meaning as otherwise in Common Lisp. Other declarations are not recognized in version 0.0.1.