Apparently at least a few readers of this list are not set up to read
HTML mail -- at least not HTML mail as generated by MS Outlook. I am
surprised. I am responsible for my kids' online school newsletter
(*not* a technically sophisticated group), and no one seems to have a
problem with HTML mail there. (Admittedly, I don't try using U+2192
(Rightwards Arrow) there; perhaps the problems people are having are
with their Unicode or ISO-2022 implementations, not with HTML itself?)
Or perhaps some of you are "plain ASCII text" purists. Sigh.
Anyway, here is my message in plain ASCII text. So much for trying to
use HTML to present things in a more readable way.
----------------------------
In private email, Dan Stanger asked about the semantics of ev; since
the answer may be of general interest, I'm sending it to the list.
Here's the specific issue that Dan came up against. The following
sequence works as expected:
nounversion: 'integrate(x,x)$
verbversion:
subst(verbify('integrate),nounify('integrate),nounversion)$
ev(verbversion);
=> x^2/2
but if you try to combine the last two steps, it doesn't:
ev(subst(verbify('integrate),nounify('integrate),nounversion))
=> 'integrate(x,x)
So how can you combine the last two steps? ... Well, the quick answer
is:
ev(subst(verbify('integrate),nounify('integrate),nounversion),eval)
=> x^2/2
Using the command-line notation:
subst(verbify('integrate),nounify('integrate),nounversion),eval
=> x^2/2
But why should this be true? Understanding it requires some discussion
of the semantics (if you can call them that) of ev.
------------------------------------------------------------------------
--------
The first thing to know about ev is that the command line ...,... is
precisely equivalent to ev(...,...). Nota bene: the ...,... syntax
for ev is not available anywhere else in Maxima. Also, the command
line ... (with no commas) is confusingly not in general equivalent to
ev(...).
The basic intuition behind ev is not evaluation, despite its name. It
is modification of evaluation. That is, x^2,x=3 or ev(x^2,x=3) should
be thought of as something like: interpret the expression x^2 in the
context where x=3. A better name for ev might have been
with_modifiers. But ev actually has a very broad range of functions,
all based on the convenience of the "," syntax.
For the remainder of this mail, I use the syntax ev(x,y) instead of
the command-line syntax x,y for clarity.
The documentation says that ev "is one of MACSYMA's [sic] most
powerful and versatile commands." Spin-free version: it is sprawling,
confusing, and inconsistent, full of misfeatures, warts, and bells and
whistles (cf.
http://developer.syndetic.org/query_jargon.pl?term=feature).
The documentation describes these inconsistencies pretty accurately,
so you can't call them bugs (a documented bug is a feature, after
all). But I would certainly call them errors at the conceptual or
design level.
The confusing thing in Dan's example is that one might expect ev to be
the Maxima equivalent of Lisp eval; it is not. Its main argument is
normally only evaluated once (with caveats, see below). Let me
demonstrate. I'll define a function which helps count evaluations:
counteval(n):= funmake('counteval,[n+1])$
Now, with normal evaluation:
counteval(0) => counteval(1)
'counteval(0) => counteval(0)
And with ev:
ev(counteval(0)) => counteval(1)
In addition, ev lets you control evaluation more finely (I have set
noundisp:true so that noun forms are displayed with a quotation mark
'):
ev(counteval(0),noeval) => counteval(0)
ev(counteval(0),eval) => counteval(2)
Note that you can't use ev(ev(x)) instead of ev(x,eval).
ev(counteval(0),eval,eval) => counteval(3)
ev(counteval(0),infeval) => infinite loop
ev('counteval(0)) => 'counteval(0)
ev('counteval(0),eval) => 'counteval(0)
eval doesn't affect nouns; recall that 'f(x) means
apply(nounify('f),[x]), which is different from
('f)(x), which means apply('f,[x]).
ev('counteval(0),counteval) => counteval(1)
Think of this as meaning "evaluate ... while treating the noun-form
of
counteval as a verb form".
ev('counteval(0),nouns) => counteval(1)
So far, so good. The scheme is fairly consistent, simple, and
understandable. But if we look more closely, we discover that there
are some strange things going on:
'(counteval(0)) => counteval(0) OK
ev(counteval(0)) => counteval(0) OK
ev('(counteval(0))) => counteval(1) ?!
similarly
'('4) => '4 OK
ev('('4)) => 4 ?!
and also
count:100$
counteval('count) => counteval(count+1) OK
ev(counteval('count)) => counteval(101) ?!
and
ev(counteval(0),noeval) => counteval(0)
OK, no evaluation
ev(counteval(count),noeval) => counteval(100)
?! variable eval'd, but not function
Well... it turns out that symbols and quoted expressions are
special-cased. They get pre-evaluated in ev - even with noeval. This
is a feature, to allow things like ev(%,numer) or ev(d23/d24,numer)
instead of requiring ev(''%,numer) and ev(''(d23/d24),numer). But the
result is that the semantics are a mess.
Here is another, more obscure, peculiarity:
ev(rectform(%e^%i)) => cos(1)+%i*sin(1) OK
ev(%e^%i,rectform) => cos(1)+%i*sin(1) OK (rectform is an evfun)
ev('rectform(%e^%i)) => 'rectform(%e^%i) OK
ev('rectform(%e^%i),rectform) => 'rectform(%e^%i) ?!
ev('rectform(%e^%i),'rectform) => cos(1)+%i*sin(1) Phew!
The problem here is that a function is an evfun, you can't use the
normal syntax for de-nounifying it. This is an example of peculiar
interactions.
ev does a host of other clever things which make its semantics very
very obscure. Most of these clever things make sense in some specific
(and maybe even common) context, but they don't generalize well, and
they often interact in peculiar ways. It only makes things worse that
the documentation describes what ev does operationally - at a sort of
program design language level - but it never actually discusses the
concepts.
So let me try to summarize conceptually (some of) what ev does:
-- defines the evaluation context of its main argument (setting flags,
binding variables, etc.)
ev(%pi/2,numer=true)
ev(%pi/2,numer)
short form of previous
ev(sqrt(x^2-1),x=2)
-- substitutes global variable values
ev(%)
-- controls evaluation (none, one, multiple, infinite)
ev(...,eval)
de-nounifies
ev('integrate(x,x),integrate)
ev('integrate(x,x),nouns)
-- performs substitutions
ev(a*x+b=y,x=(y-b)/a)
-- applies functions to the expression (ratsimp, ...)
ev((x-1)*(x+1)+1,ratsimp)
So why is all this functionality in one messy function, instead of in
multiple clean functions? Convenience. The command-line syntax x,y
lets you access all sorts of functionality at the cost of typing just
one comma (,), and no strain on the memory to remember special-purpose
functions. It is, after all, easier to write
asin(1)
%,numer
and expect it to do the right thing rather than
asin(1)
block([numer:true],''%)
(though float(%) is probably what you really want) and
d23,q^2=r^4,integrate
rather than
apply_nouns(subst(q^2,r^4,d23),integrate)
(actually, apply_nouns: (a) is not currently implemented and (b) isn't
documented to allow specifying particular nouns to apply).
I think it would be worthwhile to think of a better scheme for all
this. Something easy to use for the simple cases, but consistent in
the more complex cases.
In the meantime, if you really want just eval, and nothing else, you
can't use ev or even ev(...,eval); instead, use the internal Maxima
evaluator directly, ?meval(...).
-s