Ok. Here's my take in addressing the issues. I am not 100% happy with the docstrings, but can't seem to do better right now. If someone else feels up to it, input much appreciated.
Notable differences aside from the docstrings:
1. :TEST and :KEY syntax:
(switch (foo :test test) (bar :bar))
means to test with (FUNCALL TEST #:VALUE BAR), not (TEST #:VALUE BAR).
2. NIL as :KEY is the same as identity, as usual.
I additionally considered getting rid of T as a special clause marker, but I suppose paralleling CASE &co makes sense. I adjusted the tests to the new syntax, but more tests still need to be added.
As for the fallthrough/multiple keys issue: something like this can be done:
(switch (foo :test (lambda (x y) (member x y :test #'equal))) ('("foo" "bar") ...) (...))
but it is ugly as hell. Not sure about what to do. Re. expanding into COND: I think the API is the important bit. If we are able to figure out that something else is more efficient, then we can expand into something else -- COND is an implementation detail.
Here are the new (proposed) docstrings:
macro switch (&whole whole (keyform &key (test #'eql) key) &body clauses) "Conditionally executes one of the clauses, and returns its values. KEYFORM is first evaluated, and if KEY is given and non-null, it is used to extract a comparison value from the result of evaluating KEYFORM.
Clauses are of the form:
(CLAUSE-KEY &BODY CLAUSE-FORMS)
Each clause is tested in turn. The first clause for which
(FUNCALL TEST COMPARISON-VALUE CLAUSE-KEY)
evaluates to true has its CLAUSE-FORMS executed as an implicit PROGN, and the values returned. No further clauses are executed.
If no clause matches, and the final clause has the symbol OTHERWISE or T as its CLAUSE-KEY, it is executed. If no OTHERWISE clause exists, NIL is returned.
To use T as a regular CLAUSE-KEY, quote it:
(switch (foo) ('t 1) (t 0)) ; evaluates to 1 if FOO evaluates to T, and 0 otherwise."
macro eswitch (&whole whole (keyform &key (test #'eql) key) &body clauses) "Conditionally executes one of the clauses, and returns its values. KEYFORM is first evaluated, and if KEY is given and non-null, it is used to extract a comparison value from the result of evaluating KEYFORM.
Clauses are of the form:
(CLAUSE-KEY &BODY CLAUSE-FORMS)
Each clause is tested in turn. The first clause for which
(FUNCALL TEST COMPARISON-VALUE CLAUSE-KEY)
evaluates to true has its CLAUSE-FORMS executed as an implicit PROGN, and the values returned. No further clauses are executed.
If no clause matches, an error is signalled."
macro cswitch (&whole whole (keyform &key (test #'eql) key) &body clauses) "Conditionally executes one of the clauses, and returns its values. KEYFORM is first evaluated, and if KEY is given and non-null, it is used to extract a comparison value from the result of evaluating KEYFORM.
Clauses are of the form:
(CLAUSE-KEY &BODY CLAUSE-FORMS)
Each clause is tested in turn. The first clause for which
(FUNCALL TEST COMPARISON-VALUE CLAUSE-KEY)
evaluates to true has its CLAUSE-FORMS executed as an implicit PROGN, and the values returned. No further clauses are executed.
If no clause matches, a continuable error is signalled."
Complete diff attached.
Cheers,
-- Nikodemus