Functions

Functions in the GCL are rules for taking objects of specified data types, and using them to construct and return an object of a (possibly different) specified data type. All statements in the GCL are built up out of function calls. In this section, we desribe the rules for using functions in the GCL.

A function call consists of the name of a function followed (if the function has parameters) by a list of parameters to the function, enclosed in brackets. Functions return a value, which may in turn be used as a parameter to another function call, allowing more complex computations to be expressed.

Functions in the GCL can be either built-in or user defined. Also functions can either require arguments or not. This leads to four combinations. We label these as follows

Table 3. Function types

DescriptionBuilt-inParameters
ConstantYesNo
Built-in FunctionYesYes
VariableNoNo
User-defined FunctionNoYes

Constants

The simplest type of a function in the GCL is a constant. A constant is a built-in function that has no arguments, and returns the same value whenever it is called. How to construct constants for the basic data types is described in the section on data types. Some examples of constants follow:

Table 4. Constants

Data typeConstant
BOOLEANTrue, False
NUMBER (rational precision)1, -1234567/563
NUMBER (float precision)1., -1.234567
TEXT"Hello, world!"
OUTPUTStdOut, NullOut
INPUTStdIn

You typically don't think of a constant as a function, but it is. It is a function with no arguments, whose name is the constant itself. You can ``execute'' a constant by simply typing its name. When you do so, it returns a value, corresponding to the object that the constant represents.

You are now ready to write your first GCL program:

GCL1:= << "Hello, world!"
"Hello, world!"

It is important to note that like all functions in the GCL, constants have a data type, which is determined by the function name. If you try and use a NUMBER constant, say 123, where a TEXT or other data type is expected, you will get an error.

Variables

A variable is like a constant in that it has no arguments. It stores one object of a particular data type, and returns it when called. The difference between a constant and a variable is that a variable must be created before it can be used, and once it is created, it can be deleted or redefined if you want. To create a new variable, or to redefine an existing variable, one can use the built-in function, Assign, which has the prototype:

Assign[name->TEXT, value<->T] =: T
for any type T. (How to read a function's prototype is described in more detail in the section of function calls. For now, suffice it to say that Assign takes two parameters, the first of which is the name of the variable, and the second of which is the value to be assigned to the variable.) The variable name can be any string of alphanumeric characters (a-z, A-Z, or 0-9) beginning with a letter. So, to create a variable with name x which returns the NUMBER 1, we can use Assign as follows:
GCL1:= << Assign["x",1]
1
The Assign function also has an infix operator form, written :=. In this form, the quotes are not needed around the variable name, and the function is not ``listable'' (see the section on Lists, later in this chapter). So, equivalently, and more compactly,
GCL1:= << x := 1
1

The Assign function can be used to either create a new variable or modify an existing one. However, it cannot be used to change the type of a variable. Consider the sequence

y:=2
y:=3
y:="3"
The first call creates a new variable y of type NUMBER, with value of 2. The second modifies \verb+y+ to have value 3. Since the new assignment does not change the type of \verb+y+, this command is fine. The third statement attempts to change the data type of \verb+y+. This statement will result in an error, since Assign cannot be used to change the type of a variable. To change the data type of a variable, it must first be deleted, and then reassigned.

An existing variable may be deleted by the use of the UnAssign function, with the following prototype:

UnAssign[name->TEXT] =: BOOLEAN
After UnAssign is called on a variable, the variable is no longer defined. A subsequent call of Assign may redefine the variable to be of any type. UnAssign has a short form of := followed immediately by a linefeed or semicolon. Hence, if \verb+y+ is a variable of type BOOLEAN you can change it to NUMBER with value 3.0 in two steps as follows:
y:=
y:= 3.0

Global and static variables

Variables in the GCL are by default only visible in the the part of the program in which they are defined. In otherwords, a variable defined outside of a function will not be visible inside a GCL function (unless it is passed by reference), and a variable defined in a function will not be visible from outside the function. Secondly, variables defined inside a function are deallocated when control leaves the function. Thus, when a function is called a second time, variables defined within that function will not ``remember'' the values they were assigned in the last call to the function.

To modify the default scope and visibility of variables, the GCL uses the prefix $ in a variable name to represent a ``static'' variable and the prefix $$ to represent a global variable. A static variable is only visible in the function in which it is defined, but remains allocated after program control leaves the function, and retains its last value when the function is called again. A global variable remains allocated and visible when control passes to any function. The following example illustrates the use of static variables:

NewFunction[Foo[x->NUMBER]=:NUMBER,If[!IsDefined[$y],$y:=x];$y;];
GCL2:= << Foo[3]
3
GCL3:= << Foo[2]
3
and the following illustrates the use of global variables:
$$x:=5
NewFunction[Foo[],$$x;];
GCL1:= << Foo[]
5

Built-in functions

Built-in functions in the GCL are just like the mathematical notion of a function. They are rules which associate with each point in the domain, a point in the range. In the case of the GCL functions, each function has a list of arguments. Each argument must be of a specific data type. A point in the domain is specified by specifying a value of the correct data type for each argument of the function.

To execute a GCL function you write the function name, followed (if the function is not a constant) by a comma separated list of the arguments, enclosed in square brackets.

All of the built-in functions in the GCL are listed in the Function Reference section of the manual. For each function, the function prototype is listed. The function prototype is a template that is used to remind you of the correct syntax for each function.

A function call consists of the name of a function, and a list of parameters upon which the function is to operate. Functions return a value, which may in turn be used as a parameter to another function call, allowing more complex computations to be expressed.

A simple example of a function call is

Plus[x->1, y->2]
This calls the function named Plus, with parameter \verb+x+ set to the value 1 and \verb+y+ set to the value 2. Since Plus is the function for addition of two integers, the value returned would be, as you might expect, 3.

In the addition example above, we called a function which is listed in the function reference as

Plus[x->NUMBER, y->NUMBER] =: NUMBER
This listing of a function is called its {\it prototype}. It contains the function's name, its list of parameters, and its return value. In this case, the function Plus takes two parameters, the first named \verb+x+ and taking a value of type NUMBER and the second named \verb+y+ and taking a value of type NUMBER, and returns a value of type NUMBER.

Because the parameters have explicit names, it is possible to rearrange the order in which parameters are listed when a function is called. Thus, we could write out sample call equivalently as

Plus[y->2, x->1]
and achieve the same effect.

Explicitly specifying the formal names for parameters all the time will prove tedious and can hinder readability. For these reasons, it is also legal to specify parameters without their associated formal names. These are called passing {\it anonymous} parameters. Our addition example could thus also be written

Plus[1, 2]

When used without specifying the formal names, however, function calls are restricted to specifying parameters in exactly the same order as listed in the function prototype. In our example, the GCL interpreter would have no way of distinguishing whether we meant 1 to be the value of \verb+x+ or the value of \verb+y+, and vice versa. While in the case of addition we may flip the values of the parameters without having an effect on the result, in general this is not the case.

It is permitted to mix the two styles of parameter specification, subject to the following rules: \begin{itemize} \item All anonymous parameters must be specified before any named parameters \item No parameters may be omitted in the anonymous parameter list. If $k$ parameters are specified anonymously, they must match one-for-one the first $k$ parameters in the function's prototype. \item Once a named parameter has been specified, all succeeding parameters must be named, even if the first named parameter appeared in the same place in the parameter list as it does in the prototype. \end{itemize} \noindent Therefore, it would be legal to write

Plus[1, y->2]
but
Plus[x->1, 2]
is illegal since it violates the third condition.

To be more precise, the function Plus comes in several different variations listed in the function reference: \begin{verbatim} Plus[x->NUMBER, y->NUMBER] =: NUMBER Plus[x->TEXT, y->TEXT] =: TEXT Plus[x->MIXED, y->MIXED] =: MIXED Plus[x->BEHAV, y->BEHAV] =: BEHAV \end{verbatim} This is an example of function {\it overloading}. This means that one function name may have several possible parameter lists, sometimes called {\it signatures}. The GCL interpreter is capable of determining which version of the function to use by analyzing the names and types of the parameters used.

Since a function may have multiple signatures, it is conceivable that a function call might be ambiguous, in the sense that its parameters match more than one signature for that function. However, no function call that is complete may be ambiguous from the way that signatures have been chosen for the predefined functions. Any function call flagged by the interpreter as ambiguous must be missing at least one parameter.

Some functions have parameters which are optional, in the sense that they need not be specified in order to call the function. These parameters are indicated in the function's prototype by being surrounded by curly braces. (Note that these braces should not be included in the function call when specifying an optional parameter.) If an optional parameter is left unspecified in a function call, a default value is assumed, as given in the function's documentation.

For a function, all required parameters always precede any optional parameters. Optional parameters may also be specified anonymously, subject to the above rules on parameter specification.

All parameters so far have been passed by {\it value}, that is, a copy of the value of the parameter is given to the function to which it is passed. These parameters may not be modified by the function. It is also possible to have parameters to a function passed by {\it reference}. This means that the function does not receive a copy of the value, but rather the memory location of the value itself. Thus, the function may modify the value of a parameter passed by reference.

The symbol for passing a parameter by reference, both in a function's prototype and in a function call, is <->. Constants may not be passed by reference. Reference parameters may be specified anonymously just like a value parameter, subject to the usual rules. It is a run-time error to attempt to pass a value to a reference parameter, or vice versa.

In the case where functions have parameters which are subtypes, it is preferred to match to the subtype over the parent type. For example, suppose the following two signatures have been defined: \begin{verbatim} Foo[x->INTEGER] =: INTEGER Foo[x->NUMBER] =: NUMBER \end{verbatim} Then, a call of Foo[3] resolves to the first signature (since the value passed is an integer), and Foo[3.5] resolves to the second (since it is a number, but not specifically an integer).

User-defined functions

As GCL programs become more and more complex, frequently there are complicated operations which must be performed repeatedly. The command language therefore supports user-defined functions, which allow for defining sections of code which may be called later.

A new function can be created using the function NewFunction. For example, one might define a function to compute the absolute value of an NUMBER as such:

NewFunction[Abs[n->NUMBER],
  If[n > 0, n, If[n < 0, -n, 0]];
];
After defining the Abs function, it may be called in exactly the same way a system-supplied predefined function may. The return value of the function is the value of the last statement executed.

Parameter type matching rules apply to user defined functions in exactly the same way as to predefined functions. From the function's point of view, the parameter list is a list of variables on which assignments are automatically done at the beginning of the function execution. So, taking the Abs example above, in executing the call

Abs[42]
\noindent an assignment \verb+n := 42+ is implicitly performed before the body of the function is executed.

It is also possible to pass variables by ``reference'' to a user-defined function in the same way as a predefined function. In this case, the function's ``local'' variable is stored in the same physical location in the computer, and modifying the value locally also takes effect on the variable passed to the function. For example, it might be useful instead to define \verb+Abs+ as:

NewFunction[Abs[n<->NUMBER],
		 If[n > 0, n, If[n < 0, n := -n, 0]]]
in which case the function would still return the absolute value of \verb+n+, but also modify the variable passed to \verb+n+ to be the absolute value of the input \verb+n+. So,
q := -37;
Abs[q]
would result in the variable \verb+q+ containing the value 37 at the conclusion of execution.

Each function has its own ``scope'', or set of variables. Within a function body, the only variables which are visible are those which are declared in the parameter list of the function (this is \verb+n+ in the Abs example above), and those which are created during the function's execution. That is, no ``global'' or outside variables may be accessed directly by the function. For example, if the user typed in the following:

i := 4;
NewFunction[FooFunc[x->NUMBER], x * i]
later execution of the FooFunc would yield an ``undefined variable i'' error message, since \verb+i+ is never defined within the function. If instead FooFunc had been defined as follows:
NewFunction[FooFunc[x->NUMBER], i := 13, x * i]
FooFunc would always return 13 times the value of the parameter \verb+x+, since the value of \verb+i+ inside FooFunc is always 13, regardless of the value of \verb+i+ outside of the function.

Aliases for function calls

There are several functions which are so commonly used that special ``short forms'' are defined for them. We already saw one example with the function Assign, which has the short form :=. Most functions with short forms are the standard arithmetic and logic operators, for which the usual binary infix or unary prefix notations are supported. The example of addition used in a previous section may more familiarly be written

1 + 2
which expression is converted to the ``long form'' function call by the interpreter. Note that formal names may not be specified in a ``short form'' call, and the order of parameters is therefore significant.

Here is a list of the functions thus abbreviated, and their ``short form'' equivalents:

Table 5. Operators in GCL

FunctionOperator(s)
And[x,y]x && y, x AND y
Assign[x,y]x := y
Concat[x,y]x & y
Divide[x,y]x / y
Dot[x,y]x . y
Equal[x,y]x = y
Greater[x,y]x > y
GreaterEqual[x,y]x >= y
IntegerDivide[x,y]x DIV y
Less[x,y]x < y
LessEqual[x,y]x <= y
Minus[x,y]x - y
Modulus[x,y]x & y, x MOD y
Not[x]NOT x, !x
NotEqual[x,y]x != y
NthChar[text,n]text[[n]], text_n
NthChild[node,n]node#n
NthElement[list,n]list[[n]], list_n
Or[x,y]x || y, x OR y
Parentheses[x](x)
Plus[x]x + y
Power[x]x ^ y
Print[x]<< x
Read[in,x]in >> x
Times[x,y]x * y
UnAssign[x,y]x :=
Write[out,x]out << x

When functions are written in their canonical forms, no ambiguity arises in the order of evaluation: In order to evaluate a function, all arguments must be evaluated first. Arguments are evaluated from left-to-right as specified in the function call. This leads to a recursive structure of evaluation, which stops only when an argument being evaluated is a constant function (i. e., a function with no arguments).

When short form forms are used, then ambiguity can arise in the intended order of evaluation. For example, the statement \verb & a + b * c& , could be meant as \verb+Plus[a,Times[b,c]]+ or as \verb+Times[Plus[a,b],c]+. In order to resolve such ambiguities, all functions that have a short form representation are given an order of precedence. When a statement is parsed by the GCL, it is first scanned from left to right, replacing each short form expression at the top level of precedence with its canonical form. Then it is scanned again replacing each short form expression at the second level of precedence with its canonical form, and so on, until all short form expressions have been eliminated.

The order of precedence for built-in functions is as follows:

Table 6. Order of precedence

()
:=
>> <<
||
&&
NOT
= != < <= > >=
+ - &
* . / DIV MOD ^
(unary) + -
[[ ]] _
Thus, the statement \verb & a + b * c& would become
Plus[a,b*c]
Plus[a,Times[b,c]]
On the other hand, the statement \verb&( a + b ) * c& would become
Parentheses[a+b]*c
Parentheses[Plus[a,b]]*c
Times[Parentheses[Plus[a,b]],c]
which, since Parentheses is just the identity mapping, is equivalent to
Times[Plus[a,b],c]