Semantics of "case"



There has been a lot of discussion about the syntax of a Maxima case
statement.  Let's think about the semantics for a second.  There are
several issues.

==Kind of comparison==

One is what kind of comparison to use.  Is it syntactic equality (=)
or semantic equality (equal)?  In the case of operators (the use case
that has been discussed the most), they come to the same thing.  But
how about:

         assume(equal(x,1))$
         case  x of
             0 => und,
             1 => und,
             1/(x*(x-1))
          endcase

==Evaluation of cases==

Are the alternatives evaluated, or (implicitly) quoted?  Probably
implicitly quoted -- though as a general rule I think implicit quoting
gets you into trouble, it seems to make sense here. Any arguments the
other way?

==Result if not handled==

What if none of the clauses are true, and there is no otherwise
clause?  In "carefree" languages (to invent a term) like Lisp, the
result is usually to do nothing, or return nil.  In "cautious"
languages (like Ada or I think ML), this is an error. (Let's not even
talk about case/switch semantics in "careless" languages like C.)

I perfer the cautious approach because it makes it easy to write
first-draft code that instead of failing silently on cases that it
doesn't handle, lets you know there's a problem, e.g.

inopx(ex):=block([inflag:true],if atom(expr) then false else op(ex))$

       case opx(ex) of
              false => ex
              "+" => ...
              "*" => ...
              "^" => ...
        endcase

This will work fine for algebraic expressions, but will just return
false for sin(x) or a[2], which is almost never the right result.

But given the disagreements in other cases about silent failure vs.
error signalling, I am not sure we will agree on this....

==Multiple matches==

What about the (common) case where many alteratives are wanted in a branch, e.g.

          case op(x) of
             ["+", "-"] => ...

or

           case sign(x) of
              [pos, pz, zero] => f(x),
              [neg, nz, zero] => -f(-x),
              [pnz, pn] => analyze(x)
           end case

This sort of syntax is fine, unless you also want to allow compound
objects (like lists and sets) in the selectors, e.g.

            case (list) of
               [] => ...          /* handle empty list */
               [0] => ...       /* special case */
               ...

==Order of testing==

What if more than one branch can match?  Do we evaluate them in order? Consider

         assume(equal(x,a),equal(x,b))$
         case x of
            a => ...
            b => ...

or the sign(x) example above (where zero is in more than one clause).

The natural programming interpretation would be to evaluate the
clauses sequentially, though the natural mathematical interpretation
might be to consider the order irrelevant, and assume that if two
cases overlap, it doesn't matter which one you choose.  This also
allows the implementation more freedom.  It may also make it easier to
treat unevaluated case statements (though more on that some other
time).

Thoughts?

             -s