macro switch ((object &key test key) &body clauses) "Evaluates first matching clause, returning its values, or evaluates and returns the values of DEFAULT if no keys match."
macro eswitch ((object &key test key) &body clauses) "Like SWITCH, but signals an error if no key matches."
cswitch ((object &key test key) &body clauses) "Like SWITCH, but signals a continuable error if no key matches."
"Nikodemus Siivola" nikodemus@random-state.net writes:
macro switch ((object &key test key) &body clauses) "Evaluates first matching clause, returning its values, or evaluates and returns the values of DEFAULT if no keys match."
This really needs a sensible docstring.
It should explain that it's conceptually similiar to CL:CASE, and then how it differs. It should also explain what the :KEY arg does, and why it's useful. Last, but not least, it should contain one, or two examples.
-T.
On Sat, Jun 7, 2008 at 2:12 PM, Nikodemus Siivola nikodemus@random-state.net wrote:
macro switch ((object &key test key) &body clauses) macro eswitch ((object &key test key) &body clauses) macro cswitch ((object &key test key) &body clauses)
Documentation is insufficient: I don't see how someone can *know* how to use these from just reading the docstrings. That is easy to fix, however.
Implementation is nice enough. one could nitpick about the lack of specific condition classes, but that's not a real issue, IMO.
Tests are there, but don't test all the combinations of features.
There is now way to fall through to the next clause from the bottom of a previous one, but I don't miss that myself.
Finally, there is a question of style/consistency: unless I am mistaken, CLHS has no examples of macros with :TEST and :KEY arguments. Should the syntax be :TEST #'FOO, or :TEST FOO?
Right now I'm thinking :TEST #'FOO is better, because that allows you to refer to functions stored in local variables, etc. It also matches with DEFINE-CONSTANT does. It *is* an incompatible change, but that's why this is still ALEXANDRIA.0.DEV.
So, if we can fix the docs, add a few more tests, and change the :TEST/KEY syntax, I'm happy with these.
Cheers,
-- Nikodemus
On Sat, Jun 7, 2008 at 2:30 PM, Nikodemus Siivola nikodemus@random-state.net wrote:
There is now way to fall through to the next clause from the bottom of
s/now/no/
"Nikodemus Siivola" nikodemus@random-state.net writes:
Finally, there is a question of style/consistency: unless I am mistaken, CLHS has no examples of macros with :TEST and :KEY arguments. Should the syntax be :TEST #'FOO, or :TEST FOO?
There is RESTART-CASE's :TEST, and RESTART-BIND's :TEST-FUNCTION which contract each other on this issue.
Personally, I find RESTART-CASE's restriction for :TEST rather obnoxious. So I like the :TEST #'FOO syntax.
-T.
Documentation is insufficient: I don't see how someone can *know* how to use these from just reading the docstrings. That is easy to fix, however.
my opinion is so different than the consensus, that it probably doesn't matter much in this respect... but i'd rather spend the same time on writing documentation on how to use slime or on shaping the code. this is for developers after all, if they don't know how to C-c RET or M-. then a docstring won't save them... :) imho.
my docstring for switch would look like this: "Similar to CL:CASE but compares using a user supplied :test function." (if i was forced to write one for a 10-liner, that is... :)
Finally, there is a question of style/consistency: unless I am mistaken, CLHS has no examples of macros with :TEST and :KEY arguments. Should the syntax be :TEST #'FOO, or :TEST FOO?
switch (and other similar macros) support #'foo already. see EXTRACT-FUNCTION-NAME.
On Sat, Jun 7, 2008 at 5:02 PM, Attila Lendvai attila.lendvai@gmail.com wrote:
Documentation is insufficient: I don't see how someone can *know* how to use these from just reading the docstrings. That is easy to fix, however.
my opinion is so different than the consensus, that it probably doesn't matter much in this respect... but i'd rather spend the same time on writing documentation on how to use slime or on shaping the code. this is for developers after all, if they don't know how to C-c RET or M-. then a docstring won't save them... :) imho.
I find docstrings pretty important for Alexandria, because without them it is impossible to tell if behaviour X is a feature, bug, or a random artifact of implementation. Since the intent has always been to freeze ALEXANDRIA.1 into backwards compatibily mode, without docstrings we cannot even reasonably talk about backwards compatibility mode without being reduced to treating bugs as features.
...of course the situation is different for other libraries which are more-or-less *intended* to be moving targets -- for Alexandria the goal is to be an stable, immobile, boring, essential... and documented. :)
my docstring for switch would look like this: "Similar to CL:CASE but compares using a user supplied :test function." (if i was forced to write one for a 10-liner, that is... :)
Hah, even that would be much better then the current one. :)
Finally, there is a question of style/consistency: unless I am mistaken, CLHS has no examples of macros with :TEST and :KEY arguments. Should the syntax be :TEST #'FOO, or :TEST FOO?
switch (and other similar macros) support #'foo already. see EXTRACT-FUNCTION-NAME.
Yes, but
(defun foo (value test) (switch (value :test test) ...))
doesn't work right. EXTRACT-FUNCTION-NAME is too DWIM.
Cheers,
-- Nikodemus
"Attila Lendvai" attila.lendvai@gmail.com writes:
my docstring for switch would look like this: "Similar to CL:CASE but compares using a user supplied :test function." (if i was forced to write one for a 10-liner, that is... :)
Also, unlike CASE, it evaluates the test clauses.
Luis Oliveira luismbo@gmail.com writes:
"Attila Lendvai" attila.lendvai@gmail.com writes:
my docstring for switch would look like this: "Similar to CL:CASE but compares using a user supplied :test function." (if i was forced to write one for a 10-liner, that is... :)
Also, unlike CASE, it evaluates the test clauses.
Uh, I didn't know that. I thought it would expand into something more efficient than COND.
This means that you can't have a clause with multiple keys, right? Then I feel the lack of a fall-through facility.
-T.
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
"Nikodemus Siivola" nikodemus@random-state.net writes:
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.
ARNESI:SWITCH is more CASE-like in that it doesn't evaluate its clauses and accepts an otherwise-clause. FWIW, I've used this macro once or twice and found arnesi's behaviour more useful.
"Nikodemus Siivola" nikodemus@random-state.net writes:
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.
It isn't about expanding into COND per se, but that the clause-keys are evaluated. Both behaviours are useful, but the name SWITCH connotes the non-evaluating behaviour, IMO. We could add another macro that works like SWITCH works now, but is called SELECT.
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.
"KEYFORM is first evaluated, and if KEY is given and non-null (in which case it must evaluate to a function designator), it's applied to the result of evaluating KEYFORM, to extract the comparasion value. If KEY is not given, the result of evaluating KEYFORM itself is used as comparasion value."
"TEST must evaluate to a function designator of two arguments."
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.
I think ESWITCH, and CSWITCH shouldn't repeat everything that already said in SWITCH, but should reference SWITCH, and explain how they differ, i.e.
"ESWITCH works exactly like SWITCH except if no clause matches, an error is signalled."
-T.
On Wed, Jun 11, 2008 at 6:43 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
"Nikodemus Siivola" nikodemus@random-state.net writes:
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.
It isn't about expanding into COND per se, but that the clause-keys are evaluated. Both behaviours are useful, but the name SWITCH connotes the non-evaluating behaviour, IMO. We could add another macro that works like SWITCH works now, but is called SELECT.
On reflection, I think you are right.
Here's my next take (same docstring for all three.)
"Conditionally executes one of the clauses, and returns its values.
Clauses are of the form:
(CLAUSE-KEYS &BODY FORMS)
CLAUSE-KEYS is a designator for a list of objects. Symbols T and OTHERWISE may not be used as the CLAUSE-KEYS designator: to refer to these symbols by themselves as CLAUSE-KEYS, designators (T) and (OTHERWISE) must be used.
KEYFORM is evaluated to produce a comparison value. Each of the CLAUSES is the considered in turn. If
(MEMBER <COMPARISON-VALUE> 'CLAUSE-KEYS-LIST :TEST TEST :KEY KEY)
is true, the FORMS in that clause are evaluated as in implicit PROGN, and the values returned as the value of the SWITCH, CSWITCH, or ESWITCH form.
Clauses with T or OTHERWISE as CLAUSE-KEYS are considered to always match. Such clause must be tha last clause.
If no clause matches, SWITCH returns NIL, CSWITCH signals a continuable error, and ESWITCH signals an error."
Cheers,
-- Nikodemus
Never one to give up, I note that review cycle 2 is still on, and SWITCH &co are still on the operating table...
Aside from the docstrings -- which are quick to fix once the semantics are settled-- I think the main questions are:
* Should they evaluate clause keys or not?
* If they evaluate keys, can one specify multiple keys per clause?
* :KEY argument currently makes no sense, IMO, since (switch ((foo-bar foo)) ...) is more convenient than (switch (foo :key #'foo-bar) ...).
* :TEST and :KEY meaning. What does (defun feh (equal) (switch (*foo* :test equal) ...)) mean?
My current thinking is that evaluating keyforms is OK, as it makes it easier to write stuff like
(switch (foo :test #'equal) (constant1 ...) (constant2 ...))
I'd be happy to live with non-evaluating keys as well, though.
However, I was thinking that the convenience of multiple keyforms per clause is worth something, so, perhaps the rule could be that a keyform is either (1) an atom to be evaluated (2) a list of forms to be evaluated. So:
(switch ... (foo ; compares against the variable FOO ...) ((quux bar) ; compares against the variables QUUX and BAR ...))
However, as stated, I don't care very deeply about the evaluation vs. non-evaluation issue, and I'm happy to live with just a single keyform as well.
:KEY I don't like. I think :KEY should apply to the clause keys, and not the original object as it does now. (Or alternatively it could be dropped.)
:TEST and :KEY semantics I don't like. Currently :TEST #'FOO :TEST 'FOO and :TEST FOO all mean the same thing. This is just wrong, IMO.
I'm thinking that either it's :TEST <FUNCTION-NAME-OR-LAMBDA> like in standard macros that have keyword arguments for function, or it's :TEST <FORM-THAT-EVALUATES-TO-SOMETHING-FUNCALLABLE> like in standard functions.
Again, I'm happy with either -- but if #'FOO, 'FOO, and FOO are all acceptable, then the first means the lexically visible function FOO, second means the global function called through the symbol, and third means the function that is the value of the variable FOO.
My minimum-change proposal is to change :KEY to apply to clause keys, and not the original object, and to change :TEST and :KEY to either of the above behaviours. I'm willing to change the key evaluation semantics as well, but I don't consider that as crucial.
Comments?
Cheers,
-- Nikodemus
Random rumination on SWITCH: I think part of the evaluate-or-not confusion comes from C.
There it's common to have #define'd names as keys, which means that non-evaluation of the keys is not an issue.
One option would be to specify that keys must either be non-symbol atoms, or CONSTANTP symbols (evaluated), so
(switch (value) (numeric-constant-1 ...) (numeric-constant-2 ...))
(switch (value :test string-equal) (:keyword-1 ...) (:keyword-2 ...))
and
(define-symbol-macro foobar "foobar")
(switch (value :test equal) ("bar" ...) ("foo" ...) (foobar ...))
would all work nicely, but
(switch (value) (local-var ...))
would not be legal.
The formulation or "non-symbol atom or CONSTANTP symbol" seems pretty arbitrary, though.
Cheers,
-- Nikodemus
Hello,
I thought that the whole purpose of *switch* was to compare some value to the result of evaluated forms. I don't think it is nice to impose a restriction that avoid using evaluated variables and forms, and I didn't quite understand why it should.
As for allow either an atom or a list of forms, I don't like it very much, but it should be more or less ok. Some particular cases should be analyzed first, though, like
(switch (something) ('some-symbol ...) (*some-variable* ...) ...)
It could lead to a wrong interpretation unless you explicitly test if the first symbol of your list is *quote*. Another solution is to require the user to write more parenthesis
(switch (something) (('some-symbol) ...) (*some-variable* ...) ...)
An alternative to avoid this and other problems (e.g. excessive parenthesis) is to allow the use of some key, like:
(switch (something) ((:any value1 value2 ...) ...) ((car some-list) ...) ...)
In my opinion, this would be much better. Using the key *:member* instead of *:any* would be a good option too.
Cheers,
Gustavo.
On 2010/04/05, at 14:54 , Gustavo wrote:
Hello,
I thought that the whole purpose of switch was to compare some value to the result of evaluated forms. I don't think it is nice to impose a restriction that avoid using evaluated variables and forms, and I didn't quite understand why it should.
Yes. There should be a clear difference between SWITCH and CASE or COND.
The problems of CASE are: - it takes only literals (not even constant variables, so you'd have to use #.+cst+) - it uses EQL.
The problem of COND is that: - it doesn't compare a single expression, you have to bind it and rewrite it in each test.
As for allow either an atom or a list of forms, I don't like it very much, but it should be more or less ok. Some particular cases should be analyzed first, though, like
(switch (something) ('some-symbol ...) (*some-variable* ...) ...)
It could lead to a wrong interpretation unless you explicitly test if the first symbol of your list is quote. Another solution is to require the user to write more parenthesis
(switch (something) (('some-symbol) ...) (*some-variable* ...) ...)
An alternative to avoid this and other problems (e.g. excessive parenthesis) is to allow the use of some key, like:
(switch (something) ((:any value1 value2 ...) ...) ((car some-list) ...) ...)
In my opinion, this would be much better. Using the key :member instead of :any would be a good option too.
I don't like adding such "syntax" when a pair of parentheses would just do, or even better, if we can come out with a regular syntax that doesn't need special treatments.
With respect to the treatment of the clauses, the question is at what time should they be evaluated. In the case of CASE, it's at read-time and this is the problem of CASE. In the case of COND, it's at run-time.
We have the choice between compilation-time (coarsely) and run-time.
At compilation time we would interpret a constant symbol as its value, but all the clause keys would be taken otherwise as constants (literals).
At run-time we would admit any expression.
I think that run-time evaluation of the keys would be more discriminating from CASE so it would be good, and on the other hand, if the macro or compiler see that all the keys are actually literals, it can still do compilation time optimizations, so it wouldn't be too bad an option.
If we choose run-time evaluation, there is the question of the order of evaluation. Of course, the keys would be evaluated in sequence to stay with CL evaluation principles. But do we want short-cut evaluation, or can we imagine an algorithm where evaluating all the keys at once, and then finding the branch would be more efficient? (eg. if we could evaluate and select in parallel). In which case we would also have to deal with duplicate values for the keys, possibly in different branches. Currently, I don't see that we could provide anything better than testing sequentially, and therefore, short-cut evaluating the keys would be expected. (This is the current COND-like behavior).
Given a sensible default for :test, I find it unexpected to expect to surround the expression in parentheses. Since all the clauses are lists, I would propose to leave the keyword options at the same level:
(<switch-operator> <expression> [ :test <test-expression> ] <clauses>)
At first, I'd agree that the :key argument is useless. Even for clause keys, we can write as easily:
(<switch-operator> (k expr) :test (lambda (expr key) (test expr (k key))) <clauses>)
but then we could argue that :key would be useful, if applied to both the expression and the clause keys. The form above would be written:
(<switch-operator> expr :test (function test) :key (function k) <clauses>)
Finally, concerning the list of keys per clauses, by the same reasoning as for :key, and given the choice fo run-time evaluation of the clause keys, I'd tend to think that we can restrict ourselves to a single value. If the user wants to check against a list of keys, she can do it with a specific :test funtion:
(switch item :test (function member) ('(1 2 3) ...) ((list (get-one) (get-another)) ...) ((list *a-single-one*) ...) (otherwise ...))
Also, given run-time evaluation, I'd accept only OTHERWISE for the default clause, and leave T as a normal constant symbol to be evaluated and tested against, because T is a common value in lisp programs, while OTHERWISE is not.
At first I wouldn't make it an error to include an OTHERWISE clause in ESWITCH or CSWITCH, just a warning. For me, ESWITCH means "I cover all the cases", and if I use OTHERWISE to do so, good for me! On the other hand, I would understand the choice of making it an error. In this case, please add a mention to use SWITCH instead of ESWITCH/ CSWITCH in the error message.
In conclusion, I would like:
<switch-operator> <expression> [[ :test <test-expression> | :key <key- expression> ]] <clauses>)
<switch-operator> ::= SWITCH | ESWITCH | CSWITCH . <expression> ::= <form> . -- evaluating to any lisp object <test-expression> ::= <form> . -- evaluating to a function designator taking (at least) two arguments, -- returning a generalized boolean. -- The default is (function EQL). <key-expression> ::= <form> . -- evaluating to a function designator taking (at least) one argument, -- returning a object to be tested in place of its arguments. -- The default is (function IDENTITY).
<clauses> ::= | <clause> <clauses> . <clause> ::= ( OTHERWISE <forms> ) . <clause> ::= ( <key-form> <forms> ).
It would evaluate the <expression>, the <test-expression> if present, and then each <key-form> in turn, until a clause is selected. Then the following clause-keys won't be evaluated.
The order in which the test and key functions are called is unspecified, but they should be called at most once per source argument. (ie. key is called once for the expression, and once for each of the clause key forms that are evaluated; test is called once for each key form that is evaluated until it returns true).
The otherwise clause needs not be the last one, but its forms are evaluted only when all the tests returned NIl.
I put 'function designator', but it could be 'funcallable'. I don't know if funcallable CLOS objects are designators of functions. But my point is that the function forms are evaluated. I would reject: (switch x :test string= ...).
SCASE could be written:
(switch <string-expression> :test (function string=) ("abc" ...) ("def" ...) ... (otherwise ...))
Note that when all the strings and the test function are literals (known at compilation-time), special run-time optimizations may apply.
Nikodemus Siivola nikodemus@random-state.net writes:
Never one to give up, I note that review cycle 2 is still on, and SWITCH &co are still on the operating table...
Aside from the docstrings -- which are quick to fix once the semantics are settled-- I think the main questions are:
Should they evaluate clause keys or not?
If they evaluate keys, can one specify multiple keys per clause?
:KEY argument currently makes no sense, IMO, since (switch ((foo-bar
foo)) ...) is more convenient than (switch (foo :key #'foo-bar) ...).
- :TEST and :KEY meaning. What does (defun feh (equal) (switch (*foo*
:test equal) ...)) mean?
My current thinking is that evaluating keyforms is OK, as it makes it easier to write stuff like
(switch (foo :test #'equal) (constant1 ...) (constant2 ...))
I'd be happy to live with non-evaluating keys as well, though.
I don't have time right now to consider anything else this issue.
In past, I suggested to have both: Zetalisp contained a SELECT macro which was pretty much like SWITCH with evaluating keys, iirc. Hence I suggest to have
SWITCH, which does _not_ evaluate the keys (because of its C connotations)
and
SELECT which does.
-T.
2010/4/5 Pascal J. Bourguignon pjb@informatimago.com
[...]
I don't like adding such "syntax" when a pair of parentheses would just do, or even better, if we can come out with a regular syntax that doesn't need special treatments.
Ok, it would be ok by me if I have to use one more parenthesis to evaluate forms. If others prefer more parenthesis than more keywords, more parenthesis it is.
Regarding to not have special treatments, I think this only benefits the macro implementation, not the users, so I vote for having special treatments.
[...]
If we choose run-time evaluation, there is the question of the order of evaluation. Of course, the keys would be evaluated in sequence to stay with CL evaluation principles. But do we want short-cut evaluation, or can we imagine an algorithm where evaluating all the keys at once, and then finding the branch would be more efficient? (eg. if we could evaluate and select in parallel). In which case we would also have to deal with duplicate values for the keys, possibly in different branches. Currently, I don't see that we could provide anything better than testing sequentially, and therefore, short-cut evaluating the keys would be expected. (This is the current COND-like behavior).
I believe that run-time evaluation sequentially and short-cutting whenever the key matches some value. About evaluating and selecting keys in parallel, you mean using multiple threads? Unless the evaluation of the keys take a really long time, that would only slow down the process. If the evaluation takes a long time, the user could use multiple threads if he or she finds pertinent.
Given a sensible default for :test, I find it unexpected to expect to surround the expression in parentheses. Since all the clauses are lists, I would propose to leave the keyword options at the same level:
(<switch-operator> <expression> [ :test <test-expression> ] <clauses>)
The problem is it would be bad for automatic indentation.
A alternative would to use the model given in the link provided by Nikodemus: http://common-lisp.net/project/bknr/static/lmman/fd-flo.xml#select
but, instead of *select* and *selector*, I vote for *switch*** and *switch** :
(switch <expression> <clauses>)
(switch* <expression> <test-expression> <clauses>)
[...]
Finally, concerning the list of keys per clauses, by the same reasoning as for :key, and given the choice fo run-time evaluation of the clause keys, I'd tend to think that we can restrict ourselves to a single value. If the user wants to check against a list of keys, she can do it with a specific :test funtion:
(switch item :test (function member) ('(1 2 3) ...) ((list (get-one) (get-another)) ...) ((list *a-single-one*) ...) (otherwise ...))
To be honest, if I had to write such a test, I would rather find another way to do what I want (e.g. using cond). This looks ugly.
Also, given run-time evaluation, I'd accept only OTHERWISE for the default clause, and leave T as a normal constant symbol to be evaluated and tested against, because T is a common value in lisp programs, while OTHERWISE is not.
This would make *switch* inconsistent with *case*, but I will not object if T is treated as a normal constant symbol.
At first I wouldn't make it an error to include an OTHERWISE clause in ESWITCH or CSWITCH, just a warning. For me, ESWITCH means "I cover all the cases", and if I use OTHERWISE to do so, good for me! On the other hand, I would understand the choice of making it an error. In this case, please add a mention to use SWITCH instead of ESWITCH/CSWITCH in the error message.
I have no objections here.
I put 'function designator', but it could be 'funcallable'. I don't know if funcallable CLOS objects are designators of functions. But my point is that the function forms are evaluated. I would reject: (switch x :test string= ...).
I also vote for the test function to be evaluated. In my opinion, anything funcallable should be usable, unless someone finds a specific reason why it shouldn't be so.
SCASE could be written:
(switch <string-expression> :test (function string=) ("abc" ...) ("def" ...) ... (otherwise ...))
Note that when all the strings and the test function are literals (known at compilation-time), special run-time optimizations may apply.
-- __Pascal Bourguignon__ http://www.informatimago.com
Not to be a party pooper, but I'd like to ask for a more focused approach here in these review threads.
What are needed are -- roughly in order of priority:
1) Succinctly stated objections to the constructs as they stand.
2) Succinctly stated objections to proposed revisions.
3) Succinctly stated minor revisions.
While major revisions and additions are as such fine as well, to get through the review during this millennium it is more important to focus on throwing out things on which there isn't a clear consensus, and less important trying to perfect things that people have different ideas about.
Based on the input so far it seems to me that there is no clear current or historical consensus on how exactly SWITCH &co should work, and unless a clearly superior interface suggestion appears, I will remove them from Alexandria in a week or so, moving them to another package -- call it BYZANTIUM.
(In case there are people reading this who are not clear on it, Alexandria's purpose is not to implement every CL utility under the sun -- though it may feel like it! -- but rather implement those on whose behaviour there is a reasonable cultural consensus.)
Cheers,
-- Nikodemus
Nikodemus Siivola nikodemus@random-state.net writes:
Based on the input so far it seems to me that there is no clear current or historical consensus on how exactly SWITCH &co should work, and unless a clearly superior interface suggestion appears, I will remove them from Alexandria in a week or so, moving them to another package -- call it BYZANTIUM.
Forgive me if this has already been covered, but are there any projects currently in the wild which depend on ALEXANDRIA and its SWITCH, or is SWITCH new?
On 7 April 2010 02:02, Robert Uhl eadmund42@gmail.com wrote:
Forgive me if this has already been covered, but are there any projects currently in the wild which depend on ALEXANDRIA and its SWITCH, or is SWITCH new?
Yes, there, are. Not many that I know of, but a couple. (This does matter in this discussion some, but not very much, really.)
Cheers,
-- Nikodemus
On Tue, 6 Apr 2010, Nikodemus Siivola wrote:
Based on the input so far it seems to me that there is no clear current or historical consensus on how exactly SWITCH &co should work, and unless a clearly superior interface suggestion appears, I will remove them from Alexandria in a week or so, moving them to another package -- call it BYZANTIUM.
Maybe have an alexandria-sandbox package in Alexandria? That way discussion can stay in one local place, history is more localized, there is an obvious distinction between proposed/accepted, etc.
- Daniel
alexandria-devel@common-lisp.net