Andrei points out that:
f(q):=if q<0 then 0 else q;
plot2d(f(x),...);
gives an error because plot2d tries to evaluate f(x) with the literal
argument 'x', but that
plot2d(sin(x),...)
causes no error. (Note that I have changed the formal argument to 'q'
to avoid confusion....)
I agree that this is confusing. There are several ways you might change
this, but each has its problems (see below).
I cannot see any reasonable way to avoid this error. Worse, I can't
even see any reasonable way to give the user a better error message.
Well, the error handler could see if the error happened while a plotting
function was evaluating its arguments (evalargs), but that is very
hacky.
All I can suggest is that the documentation encourage users to
explicitly quote literal expressions in plotting functions, e.g.:
plot2d('(sin(x)),[x,0,2*%pi]) instead of plot2d(sin(x),[x,0,2*%pi])
--- Plot2d quotes its first argument ---
It would be possible for plot2d to only evaluate the expression at the
plotted x's (make it an mfexpr). But then instead of:
(C1) x^3-1$
(C2) plot2d(c1,[x,-1,1])$
(C3) plot2d([c1,diff(c1,x)],[x,-1,1])$
(C4) polarplot(expr,var):=
plot2d([parametric,expr*cos(var),expr*sin(var)],[var,0,2*%pi])$
you would have to do
(C2) plot2d(''c1,[x,-1,1])$
(C3) plot2d(''([c1,diff(c1,x)]),[x,-1,1])$ /* Parentheses are
needed */
(C4) polarplot(expr,var)::= /* Define as macro */
buildq([expr:expr,var:var],
'(plot2d([parametric,expr*cos(var),expr*sin(var)],[var,0,2*%pi]))
);
Plotting a named expression is, I think, a very common case, especially
for new users and elementary users; we should "make simple things
simple, and hard things possible".
I think it is as easy to tell people to use '(...) in one case as
''(...) in the other. Functions that quote their arguments (fexprs in
Lisp, mfexprs in Maxima) are *always* trouble to work with because you
can't compose them straightforwardly.
--- Conditional expressions return unevaluated conditionals ---
Another possibility would be to have conditional expressions return
unevaluated conditionals if they are not evaluable. So f(x) would
return something like
block([q:x],if q<0 then 0 else q)
But it is hard to see how to make that work properly. What if f were
defined as:
f(q):=block([],if q>0 then q:-q, q^5)
How does the Block pseudofunction know about the unevaluated
conditional?
--- Define f for symbolic arguments --
The reason sin(x) works is that sin works correctly on symbolic
arguments. This is because sin is in fact not defined as a programming
function with an executable definition, but as a mathematical function
with simplifications.
True, users can write executable definitions which act a bit like
mathematical functions, but it's subtle:
f(q):=if not numberp(q) then ('f)(q)
elseif q<0 then 0
else q;
f(3) => 3
f(-1) => 0
f(x^2) => f(x^2)
or even better:
f(q):=block([prederror:false,comp],
comp:is(q<0),
if comp='unknown then ('f)(q)
elseif comp then 0 else q);
f(3) => 3
f(-1) => 0
f(x^2) => x^2
But this is FAR beyond what we can expect new or elementary users to
master.
-s