We have previously mentioned that BETA strives for minimalism along with orthogonality. So far we have hidden this fact by programming in a standard fashion and presenting the programs with traditional concepts such as variables, functions, assignment statements, etc. Now we shall begin the study of BETA's particularities by considering assignment.
We have already noticed that BETA evaluates expressions left to right and uses an unusual assignment operator, ->. Our examples have shown simple examples of assignment such as:
i+1 -> i;
In BETA, we can do more: assignment is defined to operate on lists of values with single value assignment being a special case. Thus we can say:
(1,2,3) -> (i,j,k);
Which has the same effect as the series of simple assignments:
1 -> i; 2-> j; 3 -> k;
We can also cascade such assignments:
(a,b,c) -> (m,n,o) -> (x,y,z) ;
If we just consider the first items in the lists, the above statement means that we take the value of a and pass it on to m, then we take the value of m and we pass it on to x.
If the number or types of the items in lists do not match, an error is signaled:
(111, 222) -> i;
'string' -> i; (* where i is an integer *)
So far, this is all pretty obvious but, in BETA, the destination of an assignment is not restricted to being a simple variable or a list of such variables; the target of an assignment can also be a more complex object with an enter list. In that case, assignment takes place between the values in the list on the left and the variables named in the target's enter list.
If both the source S and target T of an assignment are complex objects, the assignment:
S -> T;
becomes a multiple assignment between the exit list of S, (O1, ..., Oi, ...) and the enter list of T (I1, ..., Ii, ...). In addition, the do part of S is executed before this multiple assignment and the do part of T is executed after. In other words, this happens:
For cascaded assignments:
S -> T -> U;
Note that the body of each object mentioned in the assignment is executed once. Of course, defining assignment between complex structures or objects in terms of assignment between individual exit expressions and enter variables is a recursive explanation which eventually leads to assignment between primitive objects like integers which has an obvious interpretation in terms of machine code. This is described in great detail in section 5.8 of the BETA Book.
Note that absence of a do part has no effect on assignment: it is equivalent to a null statement; but absence of an enter or and exit part has great importance. An object without an exit part cannot appear as a source in an assignment. Similarly, an object without an enter cannot appear as a target.
This property is exploited in the math fragment to define read-only objects or constants. These have only an exit list:
e: (# exit 2.7182818284590452354 #); Pi: (# exit 3.14159265358979323846 #);
BETA's generalized definition of assignment means that there is no fundamental difference between assignments and procedure calls. Following from this argument is the fact that procedure declarations and type declarations will be syntactically identical. To illustrate this, consider the following declarations. The first is a type definition for a complex numbers with two real attributes. The second is a procedure that adds reals numbers.
complex: (# Re,Im: @real; enter (Re,Im) exit (Re,Im) #); add: (# A,B: @real; enter (A,B) exit A+B #);
These definitions have been purposely made alike. There is no do part in either; the computation for add being done by an expression in the exit list. Each has 2 local real attributes. Each has an enter list and an exit list, meaning that objects of the type complex and those of the type add can be assigned values and can provide values and thus can be used on both sides of an assignment statement. For example:
(1.0, 3.3) -> complex -> (x,y); (1.0, 3.3) -> add -> x;
In the case of complex, the output value is an exact duplicate of its local state and of the input values: complex objects will be used mainly for their storage potential (as variables). With add, the output value is computed from the state (input) values: add objects are more useful for this computational aspect. In actual fact, complex would seldom be used directly in such an assignment; it would more properly be used as a model for variables which would in turn be used in assignments.
(# c1,c2: @complex; do (1.0, 3.3) -> c1; c1 -> c2; #)
The point, however, is that BETA does not distinguish between types and functions. It considers both as examples of a more fundamental concept: the object, which can be used for many things depending on how the programmer chooses to define and use it.
The program below shows the examples that we have talked about:
ORIGIN '~beta/basiclib/betaenv' ---- program: descriptor ---- (# (* Multiple assignment and function calls *) i,j,k: @integer; NINES: (# exit 99999 #); (* Constant *) complex: (# Re,Im: @integer; enter (Re,Im) exit (Re,Im) #); add: (# A,B: @integer; enter (A,B) exit A+B #); do 'Examples using multiple assignment and function calls'->putline; newline; 'Outputting a constant: NINES= '->puttext; NINES->putint; newline; newline; '(1,2,3)->(i,j,k); '->putline; newline; (1,2,3)->(i,j,k); ' I= '->puttext; i->putint; ', J= '->puttext; j->putint; ', K= '->puttext; k->putint; newline; newline; '(i,j)->(j,i): '->puttext; (i,j)->(j,i); ' I= '->puttext; i->putint; ', J= '->puttext; j->putint; newline; 'Note that (x,y)->(y,x) doesn\'t imply swap semantics: ' -> putline; newline; ' (*** More examples ***)'->putline; newline; (111,999)->complex->(i,j); (111,999)->add->k ; ' I= '->puttext; i->putint; ', J= '->puttext; j->putint; ', K= '->puttext; k->putint; newline; #)
Now for its output:
Examples using multiple assignment and function calls Outputting a constant: Nines= 99999 (1,2,3) -> (i,j,k); I= 1, J= 2, K= 3 (i,j) -> (j,i): I= 1, J= 1 Note that (x,y) -> (y,x) doesn't imply swap semantics. (*** More examples ***) I= 111, J= 999, K= 1110
|Libraries Tutorial||© 1994-2002 Mjølner Informatics||
[Modified: Thursday October 19th 2000 at 14:10]