A byte is an 8-bit unsigned integer, of possible value 0 through 255. An int is a 32-bit signed integer, while a big is a 64-bit integer. A real is a 64-bit IEEE double precision floating point number. Finally, a string represents text.
This is the complete set of basic types. For example, there is no signed byte, no unsigned int, and no 32-bit floating point representation. Note that the size of the types is precisely defined, to assist the portability of Limbo programs.
The integer types do not detect overflow or underflow; for example, adding 1 to a byte with value 255 yields a byte with value 0 and no indication that the number overflowed. The handling of overflow and other properties of reals is more sophisticated, and is discussed in Chapter XXX.
As in most languages, variables must be declared in Limbo. The declaration
i: int;
specifies i to be a 32-bit integer. (Semicolons terminate statements.)
When an integral variable is declared, its initial value is undefined. To initialize the variable as part of its declaration, use an equals sign to introduce the value:
i: int = 3;
declares i to be an integer with initial value 3. The initial value must be a constant if the variable is being declared as a global variable, outside a function, but if it is local to a function it may be initialized to any integer value. For example,
j: int = 2*i;
declares j to be an integer with initial value twice that of i at the time the declaration is executed. For example, here is a function to calculate the average of its three real arguments:
average(x, y, z: real): real { sum: real = x+y+z; sys->print("average is %f\n", sum/3.0); return sum/3.0; }
The syntax of the function should be easy to extrapolate from our ``hello, world'' example. One new aspect is the declaration of the return value of the function using a colon. Also, notice the way the formal parameters are declared: one real keyword is sufficient to declare all three parameters. That is, the line
average(x, y, z: real): real
specifies that the function average returns a real and that its parameters are x, y, and z, all reals. This line declares the variable sum to be the sum of the three arguments:
sum: real = x+y+z;
Finally, the %f notation in sys->print produces the textual representation of a floating point value, here sum/3.0. This style of formatted I/O comes from C; if it is unfamiliar, or to learn about more complicated format specifications, read the manual page sys-print(2). Table 2.1 shows the main formatting conventions.
%b | prefix for big; e.g., |
%c | character |
%d | decimal int |
%e | Like %f with e[-]digits appended |
%E | Like %f with E[-]digits appended |
%f | real as [-]digits[.digits] |
%g | real, choosing most natural of %f or %e |
%G | real, choosing most natural of %f or %E |
%r | most recent system error string; clears error |
%s | string |
%x | hexadecimal int |
One important property of Limbo is that it is strongly typed. There is no automatic conversion between types in expressions. For example, it would be illegal to compute the average as
sum/3 # error
because sum is a real and 3 is an int; we must instead divide sum by the real value 3.0. We can convert 3 to a real explicitly by casting the value: prefixing the name of a type to an expression will convert the type of the resulting expression, provided it is a legal conversion. For example, we could have written
sum / real 3;
The cast
real 3
converts the integer 3 to a floating point number with the same value, that is, to a quantity with the same value and type as 3.0.
Since the type of values and expressions is always unambiguous, Limbo can be lax about specifying types explicitly in initializations: the type of the value determines the type of the variable. For example, if we write
i: int = 3;
the specification of int is superfluous: 3 always has type int. Therefore, Limbo permits leaving the type name out altogether:
i := 3;
is the same as the previous example, a declaration of an int variable with initial value 3. (An integer constant that cannot fit in the 32 bits of an int will have type big.) Here are a couple of more interesting examples:
j := 2*i; sum := x+y+z;
The := notation is ubiquitous in Limbo programs; it is delightfully straightforward.
Almost paradoxically, the use of := means that, outside formal parameter lists, declarations rarely mention the type explicitly. Also, the declaration
i := 0;
rather than
i: int;
has the advantage that it sets the initial value of the variable.
Both = and := yield expressions that can be used within surrounding expressions. For example,
x = (y = z) + 1.0; sys->print("average is %f\n", (sum := x+y+z)/3.0);
Although it is easy to abuse such constructions, they can be helpful.
A word of caution: many languages use := as an assignment operator. In Limbo, this is emphatically not the case. A plain = is the assignment operator; := is a combination of declaration and initialization. This has ramifications; for example,
j := 2*i; j := j+1; # error
is an error because the second line redeclares j.
[XXX Section on Scope. perhaps exercise about common pitfall of j:=0; if(b){j:=1;}]
© Rob Pike and Howard Trickey 1997. All rights reserved.