Why = and := ?

The Problem

I think of := and = removing ambiguity between locals and slots on self. With a single assignment operator doing double duty for updating and initializing 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.

obj = 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 obj. 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, so lets be evil.

obj = Object clone do(
    f = method(a,
        x = a + 2
        writeln(x * 3)
    )
)


Io> obj f(1)
==> 9

Time to add some more functionality.

obj g = method(b,
    writeln(x * b)
)
obj x := 13

Lets test obj g to make sure it works.

Io> obj g(3)
==> 39

Looks good. Live editing of is good. Time to use our working object.

Io> obj f(1)
==> 9


Io> obj g(3)
==> 9

Wait a minute. Wasn't it 39 a second ago?

Yes. It was 39. But how was I to know that obj 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. (You said this the other way around. I find it helpful to think of the problem as identifying locals, rather than identify slots on self because it is the locals where the trouble shows up.) [This article was originally part of an email discussion with Trevor Fancher]

There are two types of solutions to separating out local from member variables.

Possible Solutions

Other programming languages have solved the problem of identifying local and member variables many times. Their solutions fall into two groups.

Solution 1: Explicit access for slots on self

Typically via prefixing member slots with a "self" or "this" keyword or some kind of character like "@" or "$".

Solution 2: Explicit initializing or declarating of variables

This lets the language know when a variable is being created locally instead of updating an existing slot on self. Making local variables known to the language is done in a few ways:

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. After all, it is local variables the language is trying to find. JavaScript uses the "var" keyword.

Almost all variables end up being prefixed with "var" except for a few global variables. However, local variables are common, and global variables are rare. It should be the global variables that require the extra typing, not the local variables.

To avoid typing "var" all the time, people often leave the "var" off and make everything a global. For Io, people might leave off the var and make all local variables slots on self. Which leads back to the confusion which got us here.

c) Make all initialization explicit. := and =, or some sort of "var" keyword which is always required to create a variable. Making all initialization's explicit is what Io uses at the moment.

A down side to using := and = is confusion over why both are needed. Perhaps a keyword like "var", as in, var x = 3, would have been easier to understand.