Domain of Function Argument

The definition of a function may specify the domains of input or output arguments. Domains are sets to which the values of arguments must belong. When a function is called, the interpreter first evaluate all the arguments passed, and then check if each is in the corresponding domain. If the domain check is passed, then the argument values as well as the program control are passed to the function. Otherwise a domain error is raised and the function will not be executed. When the function call is finished, the interpreter also checks if the output variable value is in the domain of the output argument. If not, the value will not be returned, and a domain error is caused.

The domain of an argument is specified in the function header using the following syntax

variable_name = default_value in domain

For example, the function header may look like

global._RP = (0+ to inf);
func = function (x = 1 in _RP,
           options = "real" in {"real", "complex") -> y in _RP
    ...
end
The above defines a function that takes two arguments x and options. _RP is the set of all positive numbers. So the argument x must be a positive number, while options must be either "real" or "complex". The output argument value y must be a positive number as well. Whenever func is called with invalid argument values, the interpreter will complain.
>> func(-5, 6) // !! domain error ...

When function definition is processed, the default value and the domain of an argument are evaluated in the scope surrounding the function definition. Therefore they can be expressions containing the local variables of the surrounding scope, but they cannot make an reference to the other argument names of the function definition. For example

x = 3;
p = function (x = x) -> (y = x)
        ...
    end
Here in (x = x) -> (y = x), the first x is the name of the formal argument, while the second and third x are the default values of arguments and refer to the value of the local variable x (whose value is 3) of the surrounding scope.

We may define a set, and then use it as the domain in a function definition immediately

Dmx = x -> (x <= -1 || x >= 2);
Dmy = x -> (x >= -1 && x <= 2);

f = function (x = 3 in Dmx, y = 0 in Dmy) -> z in _RP
           ...
    end

In practice sometimes the domains of two arguments are not independent of each other. For example

To fully represent this domain in Shang, we need to define a function of one variable, which is either a vector, or a list of x and y.
Dxy = v -> (v[0] >= 0 && v[0] <= 1 && v[1] >= 0 && v[1] <= v[0]);

f = function v = [0, 0] in Dxy -> w
           w = (1 - v[0] - v[1]) / (v[0]^2 + v[1]^2);
    end

Note that if the domain of an input argument is given, a default value must be provided. Otherwise the interpreter may find the default default value (= null) does not belong to the domain, and have difficulty processing a call to the function that provides default argument value. On the other hand, one may specify the default value but omit the domain. Whenever the domain is not specified, a default domain, which is _ALL, the set of everything, will be set as the domain of the argument.

Programming languages can roughly be divided into two categories: statically typed, in which argument value must be of a certain type, and dynamically typed, in which formal arguments can be any type. Shang has both the flexibility of dynamically typed language and the rigorousness of statically typed language. It is in fact superior to statically typed language with respect to type-checking since the domain is a set, which can be much more expressive and specific than a type. Many times, being a value of certain type doesn't guarantee that the argument is a valid one. For example, if an argument a represent age. Statical type checking only guarantees that a is an integer, while in Shang, the domain of a can be a customized set that contain all valid ages, such as integers between 0 and 150.

oz 2009-12-22