[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

SIDE-EFFECT-FREE/STATELESS Functions



The discussion on "constant functions" seems to me to have reached a
dead end.  But one good thing to come from it is the discovery that
CL provides no declaration that enables "constant folding", a very
useful optimization in quite a number of compilers.

In a more general sense, a user would want to be able to tell the
compiler that a certain named function has no internal state and that:
   (1) it has no side effects, and
   (2) its value is completely determined by its arguments.
Thus RANDOM is not side-effect-free, since it updates *random-state*; and 
while FIND-SYMBOL is side-effect-free, it is not stateless since it is 
sensitive to the setting of the global variable *package* as well as to 
the entries in the package database. AREF is both side-effect-free and
stateless.  

A compiler can perform the following optimization on a stateless and
side-effect-free function:
  (A) Calls with arguments that are compile-time constant may be "folded"
      into the literal data object evaluable at compile time;
  (B) Common subexpressions that are calls to such a function may be 
      "reduced" [in the sense described by barmar and Soley];
  (C) A call to such a function, whose return value is not consumed by
      subsequent program parts, can be eliminated [this is also true if 
      the function is merely side-effect-free];
  (D) Calls to such a function might be commutable with other program
      segments ["communting" arguments in function calls is an optimization 
      that some compilers will try to do], since the call itself to such a
      function doesn't have interactions with other program parts.

Would it be desirable to have two new declarations:  SIDE-EFFECT-FREE and
STATELESS?  Is there already a terminology somewhere in use for these 
properties?


Oddly enough, functions which return a feshly-allocated pointer to some
updatable storage are not stateless, even though they are side-effect-free 
-- CONS, APPEND, and MAKE-ARRAY are examples -- because the value of this 
kind of function is essentially an address; and that address is sensitive 
to the internal pointer to the "free list".  Consider the following:

    (let ((l '(1 2)))
      (setq a (copy-list l))
      (setq b (copy-list l)))

If COPY-LIST were stateless, then a and b should be identical; but the 
existence of RPLACA means they can't be identical in a fundamental sense.
Note that the critical argument in this paragraph depends on the word
"updatable".  For example, * is a stateless function; and in

     (let ((x (factorial 100))
           (y (factorial 200)))
       (setq a (* x y))		;surely a bignum
       (setq b (* x y)))	;surely another bignum, of same value

a and b will hold identical results.  True, they will likely be 
distinguishable by EQ, but there is _no_ update function such that an 
update to a would not be seen as an update to b also.  The EQL comparison 
is the relevant one for "identicalness", rather than EQ, even though a and 
b are both pointers to freshly-allocated storage. 

Long ago, PDP10 Interlisp provided a primitive language function called SETN
that would do just this -- it would bash the bits of a stored  number object
 -- but happily CL has nothing like this; if it did, then generic 
multiplication would not be a stateless function.



-- JonL --