Chapter 6

Working With Operators, Variables, and Assignments

You can, as you learned in the last chapter, perform arithmetic and logical operations on primitives by calling operations on the primitives. Many of these operations are so commonly used that the Telescript language provides a more intuitive syntax for them. This syntax is the operator expression which uses an operator to work on one or two values in the expression. This chapter introduces you to Telescript's operators and shows you how to use them in operator expressions.

When you create an object through an operator expression, or through other means such as a literal expression or a constructor expression, you lose access to that object for the rest of your program unless you use it as an operation argument, assign it to an attribute, or assign it to a variable. You've already learned about operations and attributes, so this chapter goes on to show you how to work with variables. You'll find detailed information about the syntax of declaration and assignment. You'll also get a close look at specifying a variable's type and determining the scope of a variable.

Operator Expressions

Telescript's operator expressions typically combine literal expressions with operators. Some of these operators perform arithmetic operations; others compare values, check the type of an object, or perform logical operations. As a result of the operator's action, the operator expression yields a single value. We'll look at each operator to see what it does.

Arithmetic Operators

Arithmetic operators perform arithmetic operations on two numbers:
OperatorDescription
-Unary minus operator. Use it before a value to negate that value.
+Addition operator. Use it between two values to add them together.
-Subtraction operator. Use it between two values to subtract the second value from the first.
*Multiplication operator. Use it between two values to multiply them together.
/Division operator. Use it between two values to divide the first value by the second value.

Some examples of operator expressions using arithmetic operators:

-872
6.43 + 9
92 - 3
25 * 4
7 / 3.5
These expressions respectively evaluate to these single values:

-872
15.43
89
100
2.0.
Note that the type of result you get using an arithmetic operator depends on the types of the number you use. If you use two real numbers, the result is a real number. If you use a real number and an integer, the result is a real number. And if you use two integers, the result is an integer--unless the operator is the division operator, which always results in a real number, even if it operates on two integers.

Comparison Operators

Comparison operators compare ordered objects--that is, any members of classes that are defined using the mix-in Ordered (described in the last chapter). Remember that one ordered object is before, equal to, or after another object--or the two objects are unordered if they can't be compared.

Comparison operator expressions evaluate to a boolean, so they are either true or false.
OperatorDescription
<Less-than operator. Use it between two values to compare the first value to the second value. If the first value is before (less than) the second value, the expression evaluates to true. If the first value is equal to, after (greater than), or unordered with respect to to the second value, the expression evaluates to false.
>Greater-than operator. Use it between two values to compare the first value to the second value. If the first value is after (greater than the second value, the expression evaluates to true. If the first value is equal to, before, or unordered with respect to the second value, the expression evaluates to false.
<=Less-than-or-equal-to operator. Use it between two values to compare the first value to the second value. If the first value is before, equal to, or unordered with respect to the second value, the expression evaluates to true. If the first value is after the second value, the expression evaluates to false.
>=Greater-than-or-equal-to operator. Use it between two values to compare the first value to the second value. If the first value is after, equal to, or unordered with respect to the second value, the expression evaluates to true. If the first value is before the second value, the expression evaluates to false.
==Equal-to operator. Use it between two values to compare the first value to the second value. If the two values are equal, the expression evaluates to true. If the two values aren't equal, the expression evaluates to false. This operator isn't limited to ordered objects; it can check equality (as defined by the operation isEqual) between unordered objects. (Note that Telescript reserves a single equal sign (=) for assignments; you must use double equal signs as the equal-to operator.)
!=Does-not-equal operator. Use it between two values to compare the first value to the second value. If the two values are not equal, the expression evaluates to true. If the two values are equal, the expression evaluates to false. This operator, like the equal-to operator, isn't limited to ordered objects.

Some examples of operator expressions using comparison operators:

3 < 8
3 > 8
3.14 <= 3.14
9 >= -5
9 == -5
9 != -5
These examples respectively evaluate to these single values:

true
false
true
true
false
true

Type-Checking Operator

This type-checking operator checks an object to see what type it is:
OperatorDescription
isType operator. Use it between an object and a type (typically the name of a class) to see if the object is a member of the class. The expression evaluates to true if the object is a member of the class (that is, an instance of the class or any of its subclasses), or to false if the object is not a member of the class. If you follow the class name with an exclamation point (!), this operator returns true if the object is an instance of the class and false if the object isn't an instance of the class.

An example of an operator expression using the is operator:

9 is Number
This expression checks to see if 9 is a member of Number. It is, so it returns true.

Another example:

9 is Number!
This expression checks to see if 9 is an instance of Number. It's not (it's an instance of Integer), so the expression returns false.

Logical Operators

These are the logical operators. They work only on boolean objects or boolean expressions (expressions that evaluate to either true or false). They evaluate to a boolean object.
OperatorDescription
!Not operator. Use it before a boolean value to perform the logical operation "not" on the value. !true evaluates to false; !false evaluates to true.
&&And operator. Use it between two boolean values to perform the logical operation "and" on the values. true && true evaluates to true; if either or both of the values are false, the expression evaluates to false.
||Or operator. Use it between two boolean values to perform the logical operation "or" on the values. false || false evaluates to false; if either or both of the values are true, the expression evaluates to true.

Some examples of operator expressions using Boolean operators:

!(5 == 5)
4 < 5 && 5 < 2
4 < 5 || 5 < 2
The first expression evaluates to false because the expression 5 == 5 evaluates to true, which is negated.

The second expression evaluates to false because the expression on the left side of the operator evaluates to true, but the expression on the right side evaluates to false.

The third expression evaluates to true because the expression on the left side of the operator evaluates to true, and only one expression needs to be true for a true evaluation.

The expressions on the right side of the logical operator && or || are not evaluated if the expression on the left side is all that's needed to determine the result. In other words, if the left expression of && is false, the full expression must be false so the right expression isn't evaluated. And if the left expression of || is true, the full expression must be true so the right expression isn't evaluated. This is important to remember if you've called an operation in the right expression; it won't be called in some cases.

Operator Precedence

A single expression can use more than one operator to tie together many values within the expression--as you just saw in the last examples. When such an expression is evaluated, each operator is applied in an order determined by Telescript's operator precedence. That precedence is determined by the priorities shown in the following table:
PriorityOperations
1! - (prefix)
2* /
3+ - (infix)
4is
5< > <= >=
6== !=
7&& ||

Operators with the highest priority within an expression are applied before operators with lower priorities. Once these operators are applied, operators of the next highest priority are applied. This continues until all the operators are applied.

To see how this works, consider the following expression:

6 + -8 * 2 == 12 / 2 - 10
This expression evaluates to false. To see why, look at the order in which the operators are evaluated. The highest priority operator is the unary minus before the eight which makes the eight a negative eight. The operators next in priority are the multiplication and division operators. Once they're resolved, the expression yields:

6 + -16 == 6 - 10
The next highest priority operators are the addition and subtraction operators which, when resolved, yield:

-10 == -4
The lowest priority operator is the equal-to operator. Negative 10 does not equal negative 4, so the expression's final value is false.

Using Parentheses

Using parentheses within an expression with more than one operator can influence the order of evaluation. Telescript applies operators within a pair of parentheses before it applies operators outside those parentheses, even if operators outside the parentheses have a higher priority than operators within. If parentheses are nested, one set within another, Telescript applies the operators within the innermost parentheses, then applies the operators within the next most nested parentheses, and so on out to operators outside all parentheses.

To see how this works, consider the following expression:

-((2+6)/2)
It evaluates to negative 4 because the addition operator is applied first, followed by the division operator, followed by the unary minus operator. Without the parentheses,

-2+6/2
the expression evaluates to 1: the unary-minus operator is applied first followed by the division operator followed by the plus operator.

An important note: Telescript doesn't specify the application order of equal-priority operators within parentheses. This means that you can't safely assume that one operator expression will be evaluated before another operator expression of equal priority--unless you enclose one of those operator expressions in parentheses.

An Overview of Variables

If you look at the statements in a typical block of code, most deal with creating objects and then working with those objects--calling operations on objects and using objects in expressions. None of this will work unless there's an important link in place between an object created in one statement and later statements that use that object. That link is a variable.

A variable is, simply put, an identifier that refers to an object. It's supported by Telescript's variable mechanism, which allows you--within a block of code--to first declare a variable and then assign a value to the variable. When you declare a variable, you create a unique identifier and specify the type of the objects to which the identifier can refer. You may then assign a value (an object) to the variable--as long as the value is a member of the specified type. Once assigned, you can reassign a new value to the variable any time, replacing the old value. You can use the variable within an expression; the expression is evaluated using the variable's current value.

Let's take a closer look at the mechanism that supports variables.

The Telescript Language's Unique Variables

If you come to Telescript programming from a background in a language like C, there are some important distinctions that you'll need to know between C variables and Telescript variables. In C, a variable has the address of a fixed range of memory. When a value is assigned to a variable, the value is written into the memory. When the variable is used later in a statement, the memory's contents are retrieved. If you assign a new value to the variable, the new value overwrites the old value in the same range of memory.

C variables are declared to be one type or another because--among other things--the amount of memory allocated for the variable is important. If you declare a variable to be a type that requires only a single byte of memory (a character, for example) and then write a value that requires four bytes (a long integer, for example), only a single byte will be stored (if the compiler lets it pass). Chances are good that the value of the variable won't be at all what you intended it to be.

In Telescript, a variable does not have an address of a specific piece of memory; it has a reference to a value, which is an object that may be located anywhere in memory. The value object's location is of no concern to the programmer. You declare a type for a variable not to allocate memory, but to assure that you know what type of object you're working with when you use the object in later statements. If you assign a new value to the variable, the Telescript engine simply changes the variable's reference from the old value to the new value.

The power of the Telescript variable is that it can handle simple values in assignments such as cost = 3.45 and sum = cost + tax, and it can also handle more complex objects such as keeping track of and working with members of the classes Place, Agent, and other similar objects. In these cases a single variable refers to a complex value that contains many objects within.

<<<Note: The current version of the Telescript engine ignores type in variable assignment. If the compiler misses a conflict of type on variable assignment, the engine will let the assignment pass. So--for example--an Integer variable may refer to a real number without any squawks.>>>

Assignment

When you assign a value to a variable, you ask the variable to refer to the value object. Once the variable is assigned, statements following can use the variable's identifier to specify the assigned value. For example, you may create a variable named sahara and assign to it an object of the custom class RainRecord (an example custom class that tallies rainfall):

sahara = RainRecord();
The right side of this statement creates a new instance of the class RainRecord for assignment to the variable. Statements following the assignment can then use the variable to work on the new object. For example, you may want to call the addTally operation on the object to add the day's rainfall totals. You do so by using the identifier, which refers to the RainRecord object and so designates the operation's responder:

sahara.addTally(.01);
You can assign a new value to a variable at any point within a block. Any statements that use the variable after reassignment work with the variable's new value. For example, if you assign a new RainRecord object to the variable sahara, all subsequent operation calls on sahara are called on the new object, not on the previously assigned object.

Scope

Variables are local to the block of code in which they're declared; a code block's variables are stored with that block. This allows statements within the block to use a variable once it's been declared. After the block is finished executing, the block's variables no longer exist. The identifiers used for the variables are dropped, references to values are voided, and (because the Telescript engine is tidy) any values created for variable assignments are dropped as well--unless they're a process or are referred to by some other object, as you'll read in later chapters.

(When an object is "dropped"--that is, it's no longer referred to--it eventually disappears. The Telescript engine's garbage-collection mechanism automatically deletes any unreferred-to objects from memory--a topic discussed in more advanced chapters of this book.)

Consider the sahara variable created in the previous example. When the variable is declared within a block, it's stored with that block as long as the block executes. Any statements that follow the declaration can use the variable. When the block finishes execution, the identifier sahara is dropped, as is the RainRecord object to which the variable refers. Because the identifier and the RainRecord object aren't processes, and because they aren't referred to by anything outside of the block, these two objects are deleted by the Telescript engine's garbage-collection mechanism, and can't be used outside of the block.

The Syntax of Declaration

You must declare a variable in a block of code before you use that variable anywhere else in the code. If you try to use a variable before you declare it, the compiler will give you an undeclared variable error and refuse to compile the code. It's good programming practice in any case to declare all variables at the beginning of a block of code.

Remember that the scope of a variable is the block within which it's declared. If you try to declare the same variable a second time within that block, the compiler will object.

Telescript declarations come in two varieties: explicit and implicit.

Explicit Type

A simple variable declaration without an object assignment must always specify a type as shown in the following example. The example creates a single variable, temperature, and specifies a type of Real. (This limits it to real-number values.)

temperature: Real;
The declaration starts with an identifier and is followed by a colon. The text of the identifier must follow all the rules set forth in the last chapter for instances of the Identifier class. On the right side of the colon is the name of a single type. The declaration ends in a semicolon.

Multiple Identifiers

A declaration may have more than a single identifier. If so, each of those identifiers creates a single variable declared to be the type specified in the declaration. (You may only specify a single type within a declaration.) You must separate the identifiers with commas and end the list of identifiers with a colon. For example

temperature, speed, volume: Real;
creates three variables all declared as type Real: temperature, speed, and volume.

Value Assignment

A declaration may optionally assign an object to its newly declared variables. If so, it follows the type with an equal sign, which is followed in turn by the value to be assigned. Remember that the value can be an expression that evaluates to a single object. Consider the following declaration with assignment:

temperature: Real = 102 - 3.4;
It creates the variable temperature, restricts its value to Real objects, and assigns it a value of 98.6. 98.6 is a real-number value that is the result of the operator expression 102-3.4. The variable temperature now refers to the object 98.6.

You may only assign a single object in a declaration. You may, however, assign that single object to more than one variable if you use more than one identifier in the declaration as shown here:

temperature, speed, volume: Real = 98.6;
All three declared variables are assigned the value of 98.6. Note that all three variables refer to the same object--the real number 98.6.

Implicit Type

Whenever you assign an object in a variable declaration, you may leave the type specification out of the declaration. The declared variable is then restricted to the type of the object you assign. For example

temperature: = 98.6;
looks like a simple assignment statement with one small difference--the colon (:) before the equal sign (=) means that it's really a declaration with an assignment. That is, it declares the variable temperature, limits it to Real objects (the type of the value 98.6), and then assigns it a value of 98.6.

In a declaration with implicit type, you use identifiers and objects just as you do in the declarations with explicit type. That is, you can use multiple identifiers if you wish such as

temperature, speed, volume: = 98.6;
and you're limited to a single object for assignment. In this example, 98.6 is assigned to all three variables.

A declaration with implicit type is a convenient form of declaration that allows you to declare a variable without knowing exactly what type you need to specify. For example, this declaration of the variable price

price: = estimate
assigns the object of the variable estimate to the variable price. That is, it refers price to the same object that estimate refers to. If estimate was declared earlier to be a real number, price is declared as a Real variable. If estimate was declared as an integer, price is declared as an Integer variable.

Explicit type declarations, on the other hand, allow you to specify a superclass of the value you assign, something you can't do with implicit type. For example, in this declaration

price: Number = 98.6;
price is restricted to the class Number, which is a superclass of both Integer and Real. You can assign 98.6 to the variable because the real number 98.6 is an object that is a member of the Number class--that is, it's an instance of Real, and Real is a subclass of Number.

Note that one drawback to using implicit type in an assignment is that it makes your code a little harder to read--you can't easily tell what type is specified for each variable. A second drawback is that if you get in the habit of using ": =" in declaration assignment, you may accidentally use it for a simple assignment and declare a new variable when you meant to use an existing variable.

Specifying a Type

Whenever you specify a type for a variable (either explicitly or implicitly), you restrict the variable's reference to objects that satisfy that type. This means that the object can refer to any object that is a member of the specified class, or--if you restrict the type as described later--an instance of the specified class.

When you explicitly specify a type, you use a class expression--a text string that gives the class name. If the class you specify is defined by a class family (such as List, for example), you must supply actuals in square braces ([]) following the class family name: List[Integer, Equal], for example. (Don't worry about class families for now; you'll read more details about families, formals, and actuals in Chapter 9.) The class you specify can be any of the built-in Telescript classes listed at the back of this book, or any custom classes that are in use when you declare the variable.

Because a variable's type may allow reference to objects of the type's subclasses, it's important to consider inheritance when you specify type. A good place to start is the beginning of Part Four of the Telescript Language Reference, which shows a class tree of built-in Telescript classes. Notice that if you specify the type Object, the variable you declare can refer to any object because Object is the root class of all other classes (except mix-ins, of course, which can't be instantiated anyway).

Once a variable is declared, you must be careful that you only assign it values that satisfy its type. If you try to assign a value that doesn't satisfy the type, the compiler won't accept the assignment.

Restricting a Type to Instance

If you use a class name by itself to specify a type, any object that is a member of that class will satisfy the type. You may, if you want, restrict the type to an instance of a class by following the class name with an exclamation point (!). Consider these two variable declarations for example:

route: Map;
locations: Map!
The first declaration specifies the class Map as its type. An instance of Map will satisfy this type, as will instances of LocalMap or WorldMap (assuming LocalMap and WorldMap are subclasses of Map). The second declaration specifies Map! as its type. Only an instance of Map will satisfy this type. Instances of LocalMap or WorldMap won't satisfy the type.

Declaring a Nil Option

The syntax of declaration offers another degree of leeway for type specification: you can add |Nil after the class expression. This specification says that you can assign a nil to the variable and the variable can be used to express a nil value. For example,

cobalt: Real|Nil
declares a variable to which you may assign a real number or a nil. If you do assign a nil, you can use the variable as a nil value in other expressions. If you hadn't declared the variable using the |Nil option, the compiler would object anytime you tried to assign a nil to the variable or anytime you tried to use the variable in an expression where the compiler knows the variable must have a value of nil.

One reason that a nil option is useful in declaration is that it allows you to check the value of a variable before it has been assigned. When the Telescript engine first creates a declared variable, it assigns the variable a nil value until you assign a value later. If you haven't declared the variable with a nil option, and then try to read the variable's pre-assignment value, the compiler will object so you won't be able to read its nil value.

The Syntax of Assignment

You can, as you just learned, assign an object to a variable when you first declare the variable. You may also wait if you want, and assign an object to a variable in a separate assignment statement. If so, the engine assigns a nil to the variable until you assign a value later.

Assignment has a simple syntax: you give the variable's identifier, follow it with an equal sign, follow that with a value for assignment, and end with a semicolon. For example, this assignment statement sets the variable spot to refer to the character "Z":

spot = 'Z';
You may also, if you wish, use multiple variables in an assignment statement such as

spot = point = location = 'Z';
in which case you must separate the variable identifiers with equal signs. All variables in a multiple assignment are assigned the same object. In this example, the three variables spot, point, and location are all assigned the value "Z"--that is, they all refer to the same character object.

It's important to make sure that the variables in an assignment are all declared somewhere in the code block before the assignment statement. It's also important to make sure that the object you assign to a variable satisfies the variable's type. If you forget either of these rules, the compiler will return errors when you try to compile your code.

When you use an assignment to assign an object to a variable, you can create a new object for the assignment, or you can use an existing object.

Creating an Object For Assignment

When you assign an object to a variable, you often create a new object for assignment. This creation takes place on the right side of the equal sign using either a literal expression or a constructor expression. A literal expression, as you learned in the last chapter, is a value such as an integer, a real number, or a string that--standing by itself--creates an object with that value.

A constructor expression is simply a class name followed by a set of zero or more values within parentheses. A constructor expression asks the engine to create an instance of the class; the values in parentheses, called initialization values, are used to define the attributes of the newly initialized object. Initialization is a topic discussed in more detail in Chapter 8. For now, it's enough to know that the class descriptions in the Telescript Language Reference describe just what initialization values are necessary for instantiating each class. You'll find that many classes require no initialization values, in which case the parentheses are empty as in this assignment example:

sahara = RainRecord();
Other classes do require initialization values, in which case the values are separated by commas and placed within the parentheses as follows:

heartwood = RingCount(35, nil, 'A');
One important consideration for constructor expressions: You can't use a constructor expression to construct a primitive object. Primitives are created exclusively through literal expressions.

Using an Existing Object For Assignment

Not all assignments need to create a new object for assignment; they can use an existing object. In that case, they use an existing variable or attribute on the right side of the assignment statement as in this example:

tab = 3;
count = tab;
In this case, the variable count now refers to the same object (3) as the variable tab. You could use this statement to get the same results:

count = tab = 3;
You can also use an assignment to calculate a new value for a variable based on the variable itself. For example

count = count + 3;
uses the operator expression count + 3 to increment the value of count by three and assign the resulting value as count's new referred object.

Determining the Scope of a Variable

As you'll recall, when you declare a variable, that variable is stored with the block of code in which it's declared. The variable exists as long as the block of code executes; when execution finishes, the variable is dropped. That makes the scope of a variable simple enough: it exists only within the block in which it's declared.

There are a few wrinkles in the fabric of scope, however, caused by the fact that a block of code can contain nested blocks of code within it. If you declare a variable at the beginning of such a block, what happens to that variable within the nested blocks of code? The answer is that the variable persists within the nested blocks. This means that you can use the variable in expressions, assign it new objects, and treat it as you would in any block--all without redeclaring it. Any new assignments you make to the variable remain in effect when the execution of the nested block is finished. Consider, for example, the following code snippet:

{
	counter: Integer = 0;
	<a statement to make the following block loop 5 times>
	{
		<statements>
		counter = counter + 1;
		<statements>
	}
	executionTimes: = counter;
}
The variable counter is declared and assigned 0 before the nested block that follows. When the nested block executes five times, counter is incremented by one each time. When the nested block is finished looping and counter is used in a statement outside the nested block, its value is 5, the last value it was assigned in the nested loop.

You can, if you wish, redeclare a variable within a nested block--that is, you declare a variable with the same identifier as a variable in the outer block. You can give the redeclared variable a new type and object assignment, and work with it as you wish. When the nested block finishes execution, the redeclared variable is dropped, and the original variable is restored for use in the outer block. To see how this works, consider the following example.

{
	counter: Integer = 73;
	<a statement to make the following block loop 5 times>
	{
		<statements>
		counter: Integer = 0;
		<statements>
	}
	howMany: = counter;
}
The first declaration in the outer block declares counter to be an integer and sets an initial value of 73. Inside the looping nested block, counter is redeclared and assigned 0. When the nested block finishes execution, counter is restored to its condition as it was before the nested block started execution; the loop value is removed and its original value of 73 is re-exposed. 73 is assigned to the variable howMany.

Interleaving Variable Declarations and Statements

Telescript, unlike many other languages, allows variable declarations to be interleaved with other statements in a block. In other words, declarations within a block don't all need to appear at the beginning of the block, before any statements.

Because variable declarations may be interleaved throughout a block, it's important to realize that the scope of a declared variable does not include any statements that precede the declaration in the block. If, for example, you declare a variable after five statements in the block, that variable is not available to the preceding five statements.

Now that you understand operators and variables, it's time to move on to the next chapter, where you'll use them to write some real Telescript code.

6 - Working With Operators, Variables, and Assignments
Operator Expressions
Arithmetic Operators
Comparison Operators
Type-Checking Operator
Logical Operators
Operator Precedence
Using Parentheses
An Overview of Variables
The Telescript Language's Unique Variables
Assignment
Scope
The Syntax of Declaration
Explicit Type
Multiple Identifiers
Value Assignment
Implicit Type
Specifying a Type
Restricting a Type to Instance
Declaring a Nil Option
The Syntax of Assignment
Creating an Object For Assignment
Using an Existing Object For Assignment
Determining the Scope of a Variable
Interleaving Variable Declarations and Statements

TS Ref - 26 JUN 1996

Generated with Harlequin WebMaker