SI413 Class 19: Expressions and assignments

Chapter 6 up to the bottom of pg 228.

Pg. 232 questions 4, 6, 7, 8

Operators vs. functions
What's the difference between an operator and a function? We have this idea that "+" is an operator, whereas "sin" is a function, for example. But the difference is language specific ... or even implementation specific. In scheme, everything is a function, pretty much, even "+". So for all practical purposes, Scheme has no operators. In our SPL implementation, there's a huge difference. Each function call results in a frame being created, whereas there's no frame created when an operator is evaluated. In Ada and C++, operators are often just semantic sugar for function calls, i.e. A + B may be literally translated as operator+(A,B), or A.operator+(B).

Prefix, postfix, infix, mixfix
Total review:
  1. a prefix operator has the operator before the operands, like ! in C/C++/Java ... if (!t) ...
  2. a postfix operator has the operator after the operands, like i++.
  3. an infix operator is a binary operator for which the operator comes between the operand, like 3 + 4.
  4. When an operator has arity greater than two and it is neither prefix nor postfix, it is usually called "mixedfix", like a ? b : c.
  5. Postscript is a language in which all expressions are postfix. It's a graphics language, and to draw a line from the "current position" to (300,400), you'd write "300 400 lineto".
  6. Scheme, of course, is all prefix. It's a special kind (what with the parens and all) called "Cambridge Polish" notation.

Associativity vs. precedence
We went over the difference between associativity and precedence ... again. You better know it for the final! Languages differ in the number of precedence levels they define. Compare the C++ operator precedence table to that of pascal:
highest        NOT
   .           *, /, DIV, MOD, AND
   .           +, -, OR
lowest         <, <=, <, >, =, >=, >, IN
Now, not only does C++ have many more operators, but it defines many more precedence levels. Too many has the potential drawback that program behavior becomes difficult to predict without constantly referring to the precedenced table. Too few levels can cause unexpected behavior. For example , in Pascal
x > y OR z > 0 is equivalent to x > (y OR z) > 0	  
which is counterintuitive to say the least!

Assignment is one of those things that's deeper than one might think. I'm not talking variable declaration, initialization or binding. That's stuff we already covered. I'm talking about assigning a new value to an existing variable.

Issue: is assignment an expression or a statment? I.e. will something like 3 + (x=5) be allowed, and what will it mean? Java and C/C++ allow this, which shows that assignements are expressions in those languages. In the above, x=5 has the side effect of assigning 5 to x, and the result of the expression is simply x after the assignement. So the type and value come directly from x. In SPL, we've defined assignement to be a statement, so something like write x := 5; is illegal. (Is this a lexical error, parse error or an error in the semantic analysis phase?) In scheme, set! returns a void value, so you can't really compute meaningfully with the return value of a set!. Thus, in essence, assignment is not an expression.

Value vs. reference model
Considering assignment brings us face to face with a fundamental language issue: What is the nature of variables? Consider:
x = x + 1;	 
is OK, but most likely
x + 1 = x;
is not. Why? Why is x something that can be assigned a value but not x + 1? Why it doesn't work depends on your model of variables. The value model thinks of variables as locations that can hold values, and one assigns values to locations. So x is a location, but x + 1 is just a value. The object on the left-hand-side of the assignment must be what's called an l-value, a location. Objects that can appear on the right-hand-side, i.e. "objects" or "values" are r-values. A variable like x can play both roles. In "x = x + 1", the x on the left stands for the location, the x on the right stands for the value stored at that location. The other model is the reference model. It views variables as being references (pointers) to actual objects. Look in the book or your notes for a nice picture to see the difference.

What are l-values
Obviously variables are l-values. It's tempting to say that other expressions aren't, but not true. The following is valid C/C++:

int x, y, z;
cin >> x;
(x > 0 ? y : z) = 10;
... which tells us that the ?: operator yields (at least sometimes) l-values. C++ even lets you "return by reference", so that the results of function calls can be l-values.
int foo(int &m, int &n) { return (n > m ? n : m); }
int& bar(int &m, int &n) { return (n > m ? n : m); }
foo(x,y) = 5; // Error, foo(x,y) not an l-value
bar(x,y) = 5; // OK
Much less exotically, though, how about X[2*i+1] = 5;? This should work, so clearly some expressions yield l-values.

In Java, types int, float, double, char all follow the value model, but everything else (everything derived from Object) follows the reference model.

Immutable objects
Often the refernce model behaves as one expects/wants. But not so much with basic types like numbers, characters and strings. For example:
int a,b,c;
a := 5;
b := 2;
c := a;
Under the value model c is still 5. Under the referenced model, if ++ really modifies the object a refers to, c now refers to 6. This is a bit counter to how we think of such things usually. With strings you have the same problem:
String a, b, c;
a := "hello";
b := "goat";
c := a;
a[0] := 'j';
Now c refers to "jello". To avoid this in these fundamental types, systems with the reference model may make these types "immutable", i.e. unchangeable. This, ++ wouldn't modify the int a points to in the first example, it would create a new int and set a to point to it. The operation in the second example would be illegal. Functions for doing this kind of operation would actually return new strings. Since Java has the value model for int's, this doesn't come into play, but strings in Java follow the reference model, and they are indeed "immutable". Check it for yourself.

More complex objects in Java follow the reference model and are not immutable. So after
BankAcct a = new BankAccount();
c = a;
c's bank account is 100, because c and a refer to the same thing. Sometimes you really want copies, though, and Java has the clonable interface to give a standard way of getting duplicate objects. If BankAcct implements clonable, we can do this:
BankAcct a = new BankAccount();
c = a.clone();
... and c's balance will still be zero ... or whatever the default is.

Christopher W Brown
Last modified: Tue Nov 17 15:07:59 EST 2009