{FnDef {FnName COND} {FnArgs CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K}}
{Type NOSPREAD NLAMBDA}
{Text
The conditional function of Interlisp, {fn COND}, takes an indefinite number of arguments, called {index COND clause}clauses.  Each {arg CLAUSE{sub i}} is a list of the form {lisp ({arg P{sub i}} {arg C{sub i1}} {ellipsis} {arg C{sub iN}})}, where {arg P{sub i}} is the predicate, and {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are the consequents.  The operation of {fn COND} can be paraphrased as:

IF
{arg P{sub 1}}
THEN
{arg C{sub 11}} {ellipsis} {arg C{sub 1N}}
ELSEIF
{arg P{sub 2}}
THEN
{arg C{sub 21}} {ellipsis} {arg C{sub 2N}}
ELSEIF
{arg P{sub 3}} {ellipsis}


The clauses are considered in sequence as follows: the predicate {arg P{sub 1}} of the clause {arg CLAUSE{sub i}} is evaluated.  If the value of {arg P{sub 1}} is "true" (non-{lisp NIL}), the consequents {arg {arg C{sub i1}} {ellipsis} {arg C{sub iN}}} are evaluated in order, and the value of the {fn COND} is the value of {arg C{sub iN}}, the last expression in the clause.  If {arg P{sub 1}} is "false" ({fn EQ} to {lisp NIL}), then the remainder of {arg CLAUSE{sub i}} is ignored, and the next clause, {arg CLAUSE{sub i+1}}, is considered.  If no {arg P{sub i}} is true for {it any} clause, the value of the {fn COND} is {lisp NIL}.

Note:  If a clause has no consequents, and has the form {lisp ({arg P{sub i}})}, then if {arg P{sub i}} evaluates to non-{lisp NIL}, it is returned as the value of the {lisp COND}.  It is only evaluated once.

Example:

{lispcode
← (DEFINEQ (DOUBLE (X)
               (COND ((NUMBERP X) (PLUS X X))
                     ((STRINGP X) (CONCAT X X))
                     ((ATOM X) (PACK* X X))
                     (T (PRINT "unknown") X)
                     ((HORRIBLE-ERROR))]
(DOUBLE)
← (DOUBLE 5)
10
← (DOUBLE "FOO")
"FOOFOO"
← (DOUBLE 'BAR)
BARBAR
← (DOUBLE '(A B C))
"unknown"
(A B C)}

A few points about this example:  Notice that {lisp 5} is both a number and an atom, but it is "caught" by the {fn NUMBERP} clause before the {fn ATOM} clause.  Also notice the predicate {lisp T}, which is always true.  This is the normal way to indicate a {fn COND} clause which will always be executed (if none of the preceeding clauses are true).  {lisp (HORRIBLE-ERROR)} will never be executed.


Note:  The {lisp IF} statement ({PageRef Tag IFstatement}) provides an easier and more readable way of coding conditional expressions than {fn COND}.
}}



{FnDef {FnName AND} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}}
{Type NOSPREAD NLAMBDA}
{Text
Takes an indefinite number of arguments (including zero), that are evaluated in order.  If any argument evaluates to {lisp NIL}, {fn AND} immediately returns {lisp NIL} (without evaluating the remaining arguments).  If all of the arguments evaluate to non-{lisp NIL}, the value of the last argument is returned.  {lisp (AND)  =>  T}.
}}


{FnDef {FnName OR} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}}
{Type NOSPREAD NLAMBDA}
{Text
Takes an indefinite number of arguments (including zero), that are evaluated in order.  If any argument is non-{lisp NIL}, the value of that argument is returned by {fn OR} (without evaluating the remaining arguments).  If all of the arguments evaluate to {lisp NIL}, {lisp NIL} is returned.  {lisp (OR)  =>  NIL}.
}}


{fn AND} and {fn OR} can be used as simple logical connectives, but note that they may not evaluate all of their arguments.  This makes a difference if the evaluation of some of the arguments causes side-effects.  Another result of this implementation of {fn AND} and {fn OR} is that they can be used as simple conditional statements.  For example: {lisp (AND (LISTP {arg X}) (CDR {arg X}))} returns the value of {lisp (CDR {arg X})} if {arg X} is a list cell, otherwise it returns {lisp NIL} without evaluating {lisp (CDR {arg X})}.  In general, this use of {fn AND} and {fn OR} should be avoided in favor of more explicit conditional statements in order to make programs more readable.



{Begin Note}
SELECT de-documented.  It doesn't seem to be used in system files (EXCEPT as a PROG tag).  Def moved to Stuff.SELECT.

Ron says:  SELECTxx fns should be conceptually like (SEL FN X . CLAUSES), where FN is wrapped around each select key (one atom or one list) so
(SELECTQ ...)  <==>  (SEL 'QUOTE ...)
(SELECTC ...)   <==>  (SEL 'CONSTANT ...)
(SELCHARQ ...)   <==>  (SEL 'CHARCODE ...)
so far so good...but unfortunately SELECT has somewhat different semantics, so it doesn't fit into this model.  In particular, (SELECT X ... ( (A B C) ...) ...) evaluates A, B, and C individually, rather than doing something to (A B C).

Also, compiled SELECT doesn't do the right thing if infix operators are in selection keys or rest of clauses.
{End Note}

{FnDef {FnName SELECTQ}
{FnArgs X CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K} DEFAULT}
{Type NOSPREAD NLAMBDA}
{Text

Selects a form or sequence of forms based on the value of its first argument {arg X}.  Each clause {arg CLAUSE{sub i}} is a list of the form
{lisp ({arg S{sub i}} {arg C{sub i1}} {ellipsis} {arg C{sub iN}})}
where {arg S{sub i}} is the selection key.
The operation of {fn SELECTQ} can be paraphrased as:

IF
{arg X} = {arg S{sub 1}}
THEN
{arg C{sub 11}} {ellipsis} {arg C{sub 1N}}
ELSEIF
{arg X} = {arg S{sub 2}}
THEN
{ellipsis}
ELSE
{arg DEFAULT}.

If {arg S{sub i}} is an atom, the value of {arg X} is tested to see if it is {fn EQ} to {arg S{sub i}} (which is not evaluated).  If so, the expressions {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are evaluated in sequence, and the value of the {fn SELECTQ} is the value of the last expression evaluated, i.e., {arg C{sub iN}}.

If {arg S{sub i}} is a list, the value of {arg X} is compared with each element
(not evaluated) of {arg S{sub i}}, and if {arg X} is {fn EQ} to any one of them, then {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are evaluated as above.

If {arg CLAUSE{sub i}} is not selected in one of the two ways described,
{arg CLAUSE{sub i+1}} is tested, etc., until all the clauses have been tested.
If none is selected, {arg DEFAULT} is evaluated, and its value is returned as the value of the {fn SELECTQ}.  {arg DEFAULT} must be present.

An example of the form of a {fn SELECTQ} is:

{lispcode
[SELECTQ MONTH
   (FEBRUARY (if (LEAPYEARP) then 29 else 28))
   ((APRIL JUNE SEPTEMBER NOVEMBER) 30)
   31]}

If the value of {lisp MONTH} is the litatom {lisp FEBRUARY}, the {fn SELECTQ} returns 28 or 29 (depending on {lisp (LEAPYEARP)});  otherwise if {lisp MONTH} is {lisp APRIL}, {lisp JUNE}, {lisp SEPTEMBER}, or {lisp NOVEMBER}, the {fn SELECTQ} returns 30;  otherwise it returns 31.


{fn SELECTQ} compiles open, and is therefore very fast; however, it will not work if the value of {arg X} is a list, a large integer, or floating point number, since {fn SELECTQ} uses {fn EQ} for all comparisons.
}}


Note:  The function {fn SELCHARQ} ({PageRef Fn SELCHARQ}) is a version of {fn SELECTQ} that recognizes {fn CHARCODE} litatoms.




{FnDef {FnName SELECTC}
{FnArgs X CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K} DEFAULT}
{Type NOSPREAD NLAMBDA}
{Text
"{fn SELECTQ}-on-Constant."  Similar to {fn SELECTQ} except that the selection keys are evaluated, and the result used as a {fn SELECTQ}-style selection key.

{fn SELECTC} is compiled as a {fn SELECTQ}, with the selection keys evaluated at compile-time.  Therefore, the selection keys act like compile-time constants (see {PageRef Fn CONSTANT}).  For example:

{lispcode
[SELECTC NUM
   ( (for X from 1 to 9 collect (TIMES X X)) "SQUARE" )
   "HIP"]}

compiles as:

{lispcode
[SELECTQ NUM
   ( (1 4 9 16 25 36 49 64 81) "SQUARE" )
   "HIP"]}
}}



{FnDef {FnName PROG1} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}}
{Type NOSPREAD NLAMBDA}
{Text
Evaluates its arguments in order, and returns the value of its first argument {arg X{sub 1}}.  For example, {lisp (PROG1 X (SETQ X Y))} sets {lisp X} to {lisp Y}, and returns {lisp X}'s original value.
}}


{FnDef {FnName PROG2} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}}
{Text
Similar to {fn PROG1}.  Evaluates its arguments in order, and returns the value of its second argument {arg X{sub 2}}.

{note arg! takes advantage of fact that evaluator evals ALL args given to a spread fn ... even excess ones.}
}}



{FnDef {FnName PROGN} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}}
{Type NOSPREAD NLAMBDA}
{Text
{fn PROGN} evaluates each of its arguments in order, and returns the value of its last argument.  {fn PROGN} is used to specify more than one computation where the syntax allows only one, e.g., {lisp (SELECTQ {ellipsis} (PROGN {ellipsis}))} allows evaluation of several expressions as the default condition for a {lisp SELECTQ}.
}}



{FnDef {FnName PROG}
{FnArgs VARLST E{SUB 1} E{SUB 2} {ellipsis} E{SUB N}}
{Type NOSPREAD NLAMBDA}
{Text
This function allows the user to write an ALGOL-like program containing Interlisp expressions (forms) to be executed.  The first argument, {arg VARLST}, is a list of local variables{index local variables} (must be {lisp NIL} if no variables are used).  Each atom in {arg VARLST} is treated as the name of a local variable and bound to {lisp NIL}.  {arg VARLST} can also contain lists  of the form {lisp (atom form)}.  In this case, {lisp atom} is the name of the variable and  is bound to the value of {lisp form}.  The evaluation takes place before any of the bindings are performed, e.g., {lisp (PROG ((X Y) (Y X)) {ellipsis})} will bind local variable {lisp X} to the value of {lisp Y} (evaluated {it outside} the {fn PROG}) and local variable {lisp Y} to the value of {lisp X} (outside the {fn PROG}).
An attempt to use anything other than a literal atom as a {fn PROG} variable will cause an error, {index ARG NOT LITATOM Error}{lisp ARG NOT LITATOM}.  An attempt to use {lisp NIL} or {lisp T} as a {fn PROG} variable will cause an error, {index ATTEMPT TO BIND NIL OR T Error}{lisp ATTEMPT TO BIND NIL OR T}.

The rest of the {fn PROG} is a sequence of non-atomic statements (forms) and litatoms (labels).  The forms are evaluated sequentially;{index PROG label} the labels serve only as markers.  The two special functions {fn GO} and {fn RETURN} alter this flow of control as described below.  The value of the {fn PROG} is usually specified by the function {fn RETURN}.  If no {fn RETURN} is executed before the {fn PROG} "falls off the end," the value of the {fn PROG} is {lisp NIL}.

{Note Example!!}
}}


{FnDef {FnName GO} {FnArgs X}
{Type NOSPREAD NLAMBDA}
{Text
{fn GO} is used to cause a transfer in a {fn PROG}.  {lisp (GO L)} will cause the {fn PROG} to evaluate forms starting at the label {lisp L} ({fn GO} does not evaluate its argument).  A {fn GO} can be used at any level in a {fn PROG}.  If the label is not found, {fn GO} will search higher progs {it within the same function,} e.g., {lisp (PROG {ellipsis} A {ellipsis} (PROG {ellipsis} (GO A)))}.
If the label is not found in the function in which the {fn PROG} appears, an error is generated, {lisp UNDEFINED OR ILLEGAL GO}.{index UNDEFINED OR ILLEGAL GO Error}
}}


{FnDef {FnName RETURN} {FnArgs X}
{Text
A {fn RETURN} is the normal exit for a {fn PROG}.
Its argument is evaluated and is immediately returned the value
of the {fn PROG} in which it appears.
}}


Note:  If a {fn GO} or {fn RETURN} is executed in an interpreted function which is not a {fn PROG}, the {fn GO} or {fn RETURN} will be executed in the last interpreted {fn PROG} entered if any, otherwise cause an error.{index ILLEGAL RETURN Error}{index UNDEFINED OR ILLEGAL GO Error}

{fn GO} or {fn RETURN} inside of a compiled function that is not a {fn PROG} is not allowed, and will cause an error at compile time.

As a corollary, {fn GO} or {fn RETURN} in a functional argument, e.g., to {fn SORT}, will not work compiled.  Also, since {fn NLSETQ}'s and {fn ERSETQ}'s compile as {it separate} functions, a {fn GO} or {fn RETURN} {it cannot} be used inside of a compiled {index NLSETQ FN}{fn NLSETQ} or {index ERSETQ FN}{fn ERSETQ} if the corresponding {fn PROG} is outside, i.e., above, the {fn NLSETQ} or {fn ERSETQ}.


{Note Is the above also true for ERRORSET in general (on which NLSETQ and ERSETQ are based)? ---mjs  <-- No! ERRORSET doesn't create a seperate function ... however, it seems that GO and RETURN won't work under a compiled call to ERRORSET anyway (compiles OK, but gives undef or illegal GO error on execution).

Heres the story: it seems that GO (and RETURN too) will not work if inside a function called (via fn call) from a compiled function containing a PROG.  If the "function" is actually a macro, so that it is compiled open, or is one of the specially-compiled fns (PROG, COND, etc.), then it works.  The important thing is the function call from a compiled function containing a PROG.  This is not immediately observable by the random user.  Should this explaination be documented??  (perhaps in compiler section??)}