I think of := and = removing ambiguity between locals and slots on self. With a single assignment operator doing double duty for updating and initialising slots it is impossible to tell if a variable is a local or on self by only looking at a method. Consider the following method:
f = method(a,
x = a + 2
writeln(x * 3)
)
Clearly x is a local. Right? Maybe not. Lets see what happens when f is shown in context.
o = Object clone do(
x = 0
f = method(a,
x = a + 2
writeln(x * 3)
)
g = method(b,
writeln(x * b)
)
)
Now it appears that x is a slot on the object o. The code for the two f methods is identical yet in one it x appears to be a local variable, and in the other x appears to be a slot on self. To work out if a variable is a local or on the object you have to read all of the code for the object.
Having to read all of the code on the whole object doesn't sound so bad does it, so let me be really evil.
o = Object clone do(
f = method(a,
x = a + 2
writeln(x * 3)
)
)
o f(1) # prints 9
# Time to add some more functionality...
o g = method(b,
writeln(x * b)
)
o x := 13
# Lets test o g to make sure it works.
o g(3) # prints 39
# Looks good. :) Live is good. Time to use our working object...
o f(1) # prints 9
o g(3) # prints 9 wait a minute... that printed 39 a minute ago...
# yes... so it was. But how was I to know that o f had a local variable called
# x?! What happens if a proto adds a slot which also happens to be the name of
# a local variable?! All hell breaks loose, that's what happens.
and so on...
The key step for me was proving to myself that := and = could not be unified because it would be ambiguous if a variable was local or not. Because locals cause the trouble, I find it useful to think of the problem as how to distinguish locals from slots, rather than distinguishing slots from locals.
There are two types of solutions.
1. Explicitly access slots on self. Typically via prefixing member slots with a "self" or "this" keyword or some kind of character like "@" or "$".
2. Explicitly marking initialisation/declaration of variables. This lets you know when a variable is being created locally instead of updating an existing slot on self.
a) C/C++, Java and so on already require variable declarations for type information so they use those declarations to indicate if a variable is local or not.
b) Use a "local" or "var" keyword to prefix local variable declaration. (It is only the local declarations we care about after all.)
c) Make all initialisation explicit. (:= and =, or a "var" keyword which is always required).