It is not tail-recursive, since the recursive call is nested in
another procedure call. However, it's almost tail-recursive;
the call to * is a tail call, and the recursive call is
its last subexpression, so it will be the last subexpression to be
evaluated.

Recall what happens when a procedure call (represented as a list of
subexpressions) is evaluated: each subexpression is evaluated, and the
first result (the procedure) is passed the other results as
arguments.[2]

Evlis tail recursion can be described as follows: when performing a
procedure call and during the evaluation of the last subexpression,
the calling environment is discarded as soon as it is not
required.[3] The distinction
between evlis tail recursion and proper tail recursion is subtle.
Proper tail recursion requires only that the calling environment be
discarded before the actual procedure call; evlis tail recursion
discards the calling environment even sooner, if possible.

An example will help to clarify things. Given fact as
defined above, say you evaluate (fact 10) and you're in
the procedure call with n = 5. The call stack of a
properly tail-recursive interpreter would look like this:

In this implementation, the last subexpression of a procedure call
is evaluated exactly like a tail expression, but the procedure call
and non-last subexpressions are pushed onto a stack. Whenever an
expression is reduced to a simple one and the stack is non-empty, a
pending procedure call with its other args are popped off, and it is
called with the reduced expression as the final argument.

Note that this didn't change the asymptotic behavior of the
procedure; it still takes \(\Theta(n)\) memory to evaluate. However,
only the bare minimum of information is saved: the list of pending
functions and their arguments. Other auxiliary variables, and
crucially the nested calling environments, aren't preserved, leading
to a significant constant-factor reduction in memory.

This raises the question: Are there cases where evlis tail
recursion leads to better asymptotic behavior? In fact, yes; consider
the following (contrived) implementation of
factorial[4]:

(define (fact2 n)
(define v (make-vector n))
(* (n (fact2 (- n 1)))))

Before the main body of the function, a vector of size \(n\) is
defined. This means that the environments in the call stack of a
properly tail-recursive interpreter would look like this:[5]

whereas the an evlis tail-recursive interpreter would keep around
only the current environment. Therefore, the properly tail-recursive
interpreter would require \(\Theta(n^2)\) memory to
evaluate (fact2 n) while the evlis tail-recursive
interpreter would require only \(\Theta(n)\)

Studying examples like the one above enabled me to finally
understand how evlin tail recursion worked and what sort of savings it
gives. However, I have yet to find a practical example where evlis
tail recursion gives the same sort of asymptotic gains as described
above, and I'd be interested to receive some. But perhaps the "large
gains" mentioned in the various papers describing it are only
constant-factor reductions in memory.

In any case, another important difference in Scheme between proper
tail recursion and evlis tail recursion is that the former is
a language feature and the latter is
an optimization. That means that it is acceptable and even
encouraged to write Scheme programs that take advantage of proper tail
recursion, but it would be unwise to rely on evlis tail recursion for
the asymptotic performance of your function. Instead, one should
treat it just as a nice constant-factor speed gain.

Note that it is easy to make evlis tail recursion "smarter." Since
Scheme doesn't specify the order of argument evaluation, an
interpreter could evaluate arguments to maximize the gains from evlis
tail recursion. As an easy example, if we had switched the arguments
to + in fact above, making it
non-evlis-tail-recursive, a smart compiler could still treat it as
such. A possible rule of thumb would be to pick a non-trivial
function call to evaluate last.

To complete the picture, I will outline below the evaluation
function for a simple evlis tail-recursive Scheme interpreter in
Javascript. All of the sources I've found describe it in terms of
compilers, so I think it'll be useful to have a reference
implementation for an interpreter.