Simple data and variables

Like most languages, Limbo has a set of basic data types from which more complex objects can be built. These basic types are byte, int, big, real, and string.

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.

%bprefix for big; e.g., %bd is decimal big
%ccharacter
%ddecimal int
%eLike %f with e[-]digits appended
%ELike %f with E[-]digits appended
%freal as [-]digits[.digits]
%greal, choosing most natural of %f or %e
%Greal, choosing most natural of %f or %E
%rmost recent system error string; clears error
%sstring
%xhexadecimal int
Table 2.1. The main formatting characters for sys->print and its relatives. For more details, including how to fine-tune the formatting layout, see the man page sys-print(2).

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;}]

previous next

© Rob Pike and Howard Trickey 1997. All rights reserved.