This is certainly an interesting design direction that’s fun to think through. You would want something like this:
:= is required to initialize a variable,
= can only be used to update an existing variable. That would take some getting used to for people coming from Python, R or Matlab, who are unaccustomed to thinking about variable declaration much. Their question would likely be “what is the difference?” — I’ll leave that to someone else to try to answer in this hypothetical alternate universe (perhaps a different version of me).
It could, as you suggested, also declare the type of the variable to be the type of whatever the value that is assigned is. One of the reasons you don’t want variables to default to being type-const is that it’s fairly common for people who are using a REPL or doing other “fast and loose” work to reuse the same variable name like
x for something different — say a float in one case and a string in another. The
= distinction could help with that: if
x := occurs twice, the second one creates a new, separate
x variable. Any previous capture of
x still reference the old version of
x (which can no longer be modified except by a closure), any new capture references the new
x with a different type. That’s slightly confusing but I think that it would be fine and people would be able to enter multiple examples in the same REPL, which is the main thing they want. And we’d have type-stable globals, which is pretty nice. So this:
x := 0
x = 1 # fine, works
x = "hello" # error, can't convert `String` to `Int`
get() = x # capture this `x`
set(v) = x = v # modify this `x`
x := "hello" # works, new `x`
get′() = x # capture new `x`
get() # returns 1
get′() # returns "hello"
x = "goodbye"
get′() # returns "goodbye"
get() # still returns 1
set(2) # only way we can change the old `x` now
get() # returns 2
So that’s pretty nice. You can reuse common variable names but
get′ get to assume consistent types, which seems good. Thinking through some examples. First closures that do and don’t modify an outer local:
x := 1
g() = x = 2 # modifies outer `x`
h() = x := 3 # doesn't affect outer `x`
Now for loops. The first version has a new iteration variable in each iteration:
for i := 1:10
# new `i` in each iteration
# `i` is undefined here
Next a version where the iteration variable is an outer local:
i := 0
for i = 1:10
# same `i` in all iterations
# `i` is 10 here
Unclear what one would do with the
in syntax that many people prefer. Do we just treat
in the same way as
:=? The assignment syntax for for loops seems strictly better in this design since it lets you choose between using an outer variable and having a new variable per loop iteration. Classic example of non-obvious interactions between different language features.
I can’t think of any other interesting example off the top of my head. Anyone think of any?