In 1990 Tom Markson and I developed a compiler for a new language we had designed, called 'bx'. The main features were:
The standard for loop in bx called a library generator "upto":
for int i := upto(1, 10) do
...
end for;
The definition of the library upto generator was:
gen int upto (int from, to) is
while from <= to do
yield from;
from := from + 1;
end while;
end gen;
Generators could return references, so the calling code could alter
the value of the variable being returned.
From the reference manual:
A take is the inverse of a function. The value assigned to the take is brought into the take body. For example:func int max(int x,y) { # func returns an int if (x > y) max:=x; # if x > y return x else max:=y; # else return y } take int max(var int x,y) { # take accepts an int if (x>y) x:=max; # will assign value to whichever variable else # is greater y:=max; } proc main { int x:=1, y:=3; # declare and initialize printf("%d",max(x,y)); # func call max(x,y):=100; # take call assert (x == 1); assert (y == 100); }Take and func may have the same name and the compiler will differentiate between them. In the take call "max(x,y):=100", the value 100 is bound to max; so x:=max actually becomes "x:=100" when the take is called.Take together with func (or give, described later) create a virtual variable. That is, a pair of func/takes may actually appear as, and operate as a standard variable. For example:
func int foo { printf("foo was called"); foo:=1; } take int foo { printf("foo was assigned to %d",foo); } foo:=1; # will generate: foo was assigned to 1 x:=foo; # will result in: foo was calledThus, with take, we have created the possibility of two-way functions. These may be used in place of standard variables. Take/func pairs are used extensively in BX even if the programmer is unaware of it. For example, an array makes use of take and func to generalize the accessing of its values by index:x[15]:=12; is identical to x.index(15):=12; y:=x[15]; is identical to y:=x.index(15);So, when you access an array, you may actually be calling a take or func to perform the work. This allows any box which has a take/func pair called index to be treated as an array, no matter what else the box contains. In other words, the programmer may allow any data structure to be treated as an array simply by providing a take/func pair called "index". Since take/func pairs are treated as if they are variables, the following are legal:x[15] := x[15] + 1; # x.index(15):=x.index(15)+1; max(x,y) := max(x,y) / 100; # becomes: # # if (x > y) # x := 100; # else # y := 100;Thus, take/func pairs may be used in expressions as long as take appears on the left side of the assignment and func appears on the right. Take and func may appear alone, however. One does not require the other.
proc do_something (int x)
ret ERROR(int,int);
ret WARNING(int);
ret SUCCESS(char);
{
const int errnum := 10, severity := 20;
if (x < 0)
return ERROR(errnum,severity);
else if (x == 0)
return WARNING(10);
else
return SUCCESS('a');
}
do_something(0) {
case ERROR(int errno,sever):
printf("Error number %d, severity %d",errno,sever);
case WARNING(int warning_num):
printf("Warning number: %d",warning_num);
case SUCCESS(char c):
printf("got the char: %c",c);
}
Some code samples from bx: