3

When reading Commom Lisp code written by other developers I've noticed that some of them call the VALUES accessor without supplying any arguments in last form of definitions whose return values are void or neglectable.

Example:

(defun definition-whose-return-values-are-neglectable ()
  ... Some s-expressions ...
  (values))

Is there any advantage (for the compiler, in performance, etc.) to add this form as the last expression in these type of definitions?

Paulo Tomé
  • 1,910
  • 3
  • 18
  • 27

3 Answers3

3

One useful thing is that you don't end up with random values printed when you call the function in the REPL. This is quite handy when interacting with the system.

Vatine
  • 20,782
  • 4
  • 54
  • 70
3

Printing

There is no invisible in Lisp, so whatever your function returns will be printed by the the P(print) part of the REPL. This means that if your print settings are inappropriate, the system may print many screenfuls (you may even get a confusing stack overflow error because your print-circle is nil and the return value is a circular structure).

Compilation

The compiler can do some optimizations (which a specific compiler may or may not actually do in practice); e.g., it may mark the function as not returning anything interesting and then compile expressions like (setq var (my-func)) to (progn (my-func) (setq var nil)).

If the compiler can prove that the function has no side effects and you end it with (values) then the compiler can just drop its invocations altogether.

Documentation

An important aspect of computer programming is that your code will be read by other humans, including yourself, and adding (values) to the end of the function tells the reader that the function is called for side effects only.

Community
  • 1
  • 1
sds
  • 58,617
  • 29
  • 161
  • 278
3

The advantages, or main use cases of a (values) last form are:

  • Self-documenting code when return values are non-sense for a definition
  • Generally, to avoid development tools from handling or keeping references to neglected or unnecessary results;
    Specifically:
    • To avoid printing long or infinite sequences in REPLs, in case you have *print-circle* set to t, *print-length* set to nil, *print-level* set to nil, or any implementation specific parameter of the like
    • REPLs set * to nil as the consequence of a no-value being treated as nil, and set / to an empty list
    • In Allegro CL's IDE, Listeners and the Trace dialog keep references to results until they're cleared 1
    • In LispWorks' IDE, the Tracer tool keeps references to results in the output data view until they're cleared
    • In SLIME, the REPL keeps references to results in presentations until they're cleared

The non-advantages/non-issues:

  • Some compilers generate a few less instructions, some generate a few more, but unless you're crunching bytes and/or cycles, this is negligible
  • You'd already need to return no values, such as:
    • When you expect the caller to discriminate on the amount of returned values
      This probably also exposes bad design of the definition
    • When the results are supposed to be used in multiple-value-call forms
      This probably also exposes bad design of the caller, which is effectively having side effects between forms
      There's little to be done here other than to get rid of side effects or to allow the side effects to happen before or after the whole multiple-value-call

The issues:

  • Generally, it disables tail call optimizations from the definition
    However, a sufficiently smart compiler may detect tail calls to no-values definitions, usually with the help of ftype declares/declaims, and apply the optimization in these calls
    This is not without the risk of the other definitions being changed, so my guess is that no implementation does it

1. You may clear a Listener's history in Allegro CL by setting the history outline's content to an empty list:

(setf (cg:range (cg:find-component
                 :history-outline
                 (first (cg:toolbars ide.base:*listener-window*))))
      '())
acelent
  • 7,965
  • 21
  • 39