[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11. Control


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.1 Output printing

When an interactive programming system has done evaluating a top-level expression, it normally prints the result, followed by a new-line. Evaluating a shell command does not yield an explicit result, but prints the standard output of the program. Shells assume that a program prints complete lines, terminated by new-lines, so an explicit new-line is not added by the shell. So if Q views the output from a program as equivalent to returning a string, then when printing a result that is a string, we should not add a new-line. But when printing other objects, it is desirable to add a new-line.

The format directive `%+s' prints the argument as suitable for printing a result. If the argument is not a sequence, it is printed, followed by a new-line.
 
sprintf "|%+s|" 3+4 ==> "|7\n|"
To print a sequence, each element of the sequence is printed in turn. Spaces are normally printed between elements. However, if neighboring elements are in turn sequences, a new-line is printed instead. Also, no space is printed after a character. (Since a string is a character sequence, it is printed without extra spaces.) Finally, a new-line is printed, unless the sequence was empty, or the last element was a character.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.2 True and false

Statements that are evaluated solely for their side-effects (such as assignments and declarations) should not print anything. Therefore, we decree that they return "nothing", that is the empty string "".

A declaration in Q is just a unification expression. (That is, there is no conceptual difference between 5=(2+3), and :x=(2+3), except that the latter also declares x (a compile-time action), and at run-time has the side-effect of binding x to 5.) Hence, a successful unification retrurn "" as its value. For consistency, other standard relations (such as 2<3) also evaluate to "" if they succeed. The default "true" value in Q is therefore "". Note also that ; is essentially "logical and."

"Exception" values represent "false." If a function argument is an exception, the function returns the exception. Exceptions are not normal first-class values, and they use a condition-handling mechanism using non-local jumps.

An "uncaught" exception is normally just printed:
 
Q1> 2<3   # True, prints nothing.
Q2> 2>3   # False, raises exception.
Exception: Failed comparison (>).

Exceptions that represent failures (but not serious errors) are caught using the if construct.
 
Q3> if 2 > 3 => "True\n" || "False\n"
False
If a program returns a non-zero exit code, that raises a failure exception:
 
Q4> sh -c "exit 1"
Exception: Program sh (pid: 5292) failed with return code 1.
Q5> if sh -c "exit 1" => "sh ok\n" || "sh failed\n"
sh failed
(One difference compared to traditional shells is that in Q a statement list only succeeds if each statement succeeds. That is, in Q, the ; operator corresponds to `sh''s &&.)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.3 Statement lists

Most languages support some kind of "statement list" form, where each "statement" is evaluated in turn. In Q, we define the effect of evaluating expr1; expr2 as the following: First evaluate expr1, then evaluate expr2. If either result is "" (or []), then return the other result. If neither is null, the result is the concatenation of the two values, as if computed by sprintf "%s%s" expr1 expr2.

This arcane definition is chosen because it has the following desirable properties:

We also define:
 
expr1
expr2
to be the same as:
 
(expr1; expr2)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11.4 Looping

Q (being a non-pure functional language) does not have standard iterative constructs. Instead it relies on various operators for manipulating sequences. The intent is to optimize these in a manner similar to [Water's streams]. A very powerful primitive is provided by the {...?...} construct, which is a lazy (demand-evaluated) sequence defined as a mapping from an index (denoted by ?) to the value of the expression in {...}.
 
Q12> ({stdout printf "<%d>" ?} for 10) do; stdout printf "\n"
<0><1><2><3><4><5><6><7><8><9>
The do operator takes any sequence (lazy or not), and "prints" each element in sequence. (Normally each element evaluates to an empty result, but the side effect is of interest.) We informally define X do for a sequence X of length n as:
 
sprintf "%s" (X 0; X 1; ...; X n-1)
(Of course n need not be known before X is exhausted.) Thus we can replace `printf' operations that write to `stdout' with ones that returns a string:
 
Q2> ({sprintf "<%d>" ?} for 10) do; "\n"
<0><1><2><3><4><5><6><7><8><9>


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Per Bothner on December, 4 2001 using texi2html