Chapter 3. Built-in Functions
3.1. Representation [1]
Up to this point, all the objects we have been concerned with have had
no internal structure discernible in MDL. While the characteristics of
objects with internal structure differ greatly, the way READ and
PRINT handle them is uniform, to wit:
-
READ, when applied to the representation of a structured object, builds and returns an object of the indicatedTYPEwith elements formed by applyingREADto each of their representations in turn. -
PRINT, when applied to a structured object, produces a representation of the object, with its elements represented asPRINTapplied to each of them in turn.
A MDL object which is used to represent the application of a function
to its arguments is an argument of TYPE FORM. Its printed
representation is
< func arg-1 arg-2 ... arg-N >
where func is an object which designates the function to be applied,
and arg-1 through arg-N are object which designate the arguments
or "actual parameters" or "inputs". A FORM is just a structured
object which is stored and can be manipulated like a LIST (its
"primitive type" is LIST -- chapter 6). The application of the
function to the arguments is done by EVAL. The usual meaning of
"function" (uncapitalized) in this document will be anything
applicable to arguments.
3.2. Evaluation [1]
EVAL applied to a FORM acts as if following these directions:
First, example the func (first element) of the FORM. If it is an
ATOM, look at its "value" (global or local, in that order -- see
next chapter). If it is not an ATOM, EVAL it and look at the
result of the evaluation. If what you are looking at is not something
which can be applied to arguments, complain (via the ERROR
function). Otherwise, inspect what you are looking at and follow its
directions in evaluating or not evaluating the arguments (chapters 9
and 19) and then "apply the function" -- that is, EVAL the body of
the object gotten from func.
3.3. Built-in Functions (TYPE SUBR, TYPE FSUBR) [1]
The built-in functions of MDL come in two varieties: those which have
all their arguments EVALed before operating on them (TYPE SUBR,
for "subroutine", pronounced "subber") and those which have none of
their arguments EVALed (TYPE FSUBR, historically from Lisp
(Moon, 1974), pronounced "effsubber"). Collectively they will be
called F/SUBRs, although that term is not meaningful to the
interpreter. See appendix 2 for a listing of all F/SUBRs and short
descriptions. The term "Subroutine" will be used herein to mean both
F/SUBRs and compiled user programs (RSUBRs and RSUBR-ENTRYs --
chapter 19).
Unless otherwise stated, every MDL built-in Subroutine is of
TYPE SUBR. Also, when it is stated that an argument of a
SUBR must be of a particular TYPE, note that this means that
EVAL of what is there must be of the particular TYPE.
Another convenient abbreviation which will be used is "the SUBR
pname" in place of "the SUBR which is initially the 'value' of the
ATOM of PNAME pname". "The FSUBR pname" will be used with a
similar meaning.
3.4. Examples (+ and FIX; Arithmetic) [1]
<+ 2 4 6>$
12
The SUBR + adds numbers. Most of the usual arithmetic functions
are MDL SUBRs: +, -, *, /, MIN, MAX, MOD, SIN,
COS, ATAN, SQRT, LOG, EXP, ABS. (See appendix 2 for short
descriptions of these.) All except MOD, which wants FIXes, are
indifferent as to whether their arguments are FLOAT or FIX or a
mixture. In the last case they exhibit "contagious FLOATing": one
argument of TYPE FLOAT forces the result to be of TYPE FLOAT.
<FIX 1.0>$
1
The SUBR FIX explicitly returns a FIXed-point number
corresponding to a FLOATing-point number. FLOAT does the opposite.
<+ 5 <* 2 3>>$
11
<SQRT <+ <* 3 3> <* 4 4>>>$
5.0
<- 5 3 2>$
0
<- 5>$
-5
<MIN 1 2.0>$
1.0
</ 11 7 2.0>$
0.5
Note this last result: the division of two FIXes gives a FIX with
truncation, not rounding, of the remainder: the intermediate result
remains a FIX until a FLOAT argument is encountered.
3.5. Arithmetic Details
+, -, *, /, MIN, and MAX all take any number of arguments,
doing the operation with the first argument and the second, then with
that result and the third argument, etc. If called with no arguments,
each returns the identity for its operation (0, 0, 1, 1, the
greatest FLOAT, and the least FLOAT, respectively); if called with
one argument, each acts as if the identity and the argument has been
supplied. They all will cause an overflow or underflow error if any
result, intermediate or final, is too large or too small for the
machine's capacity. (That error can be disabled if necessary --
section 16.9).
One arithmetic function that always requires some discussion is the
pseudo-random-number generator. MDL's is named RANDOM, and it always
returns a FIX, uniformly distributed over the whole range of
FIXes. If RANDOM is never called with arguments, it always returns
the exact same sequence of numbers, for convenience in debugging.
"Debugged" programs should give RANDOM two arguments on the first
call, which become seeds for a new sequence. Popular choices of new
seeds are the numbers given by TIME (which see), possibly with bits
modified (chapter 18). Example ("pick a number from one to ten"):
<+ 1 <MOD <RANDOM> 10>>$
4