Many languages do choose the route of making assignment a statement rather than an expression, including Python:
foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg
and Golang:
var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies
Other languages don't have assignment, but rather scoped bindings, e.g. OCaml:
let foo = 42 in
if foo = 42 then
print_string "hi"
However, let
is an expression itself.
The advantage of allowing assignment is that we can directly check the return value of a function inside the conditional, e.g. in this Perl snippet:
if (my $result = some_computation()) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
Perl additionally scopes the declaration to that conditional only, which makes it very useful. It will also warn if you assign inside a conditional without declaring a new variable there – if ($foo = $bar)
will warn, if (my $foo = $bar)
will not.
Making the assignment in another statement is usually sufficient, but can bring scoping problems:
my $result = some_computation()
if ($result) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
# $result is still visible here - eek!
Golang heavily relies on return values for error checking. It therefore allows a conditional to take an initialization statement:
if result, err := some_computation(); err != nil {
fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)
Other languages use a type system to disallow non-boolean expressions inside a conditional:
int foo;
if (foo = bar()) // Java does not like this
Of course that fails when using a function that returns a boolean.
We now have seen different mechanisms to defend against accidental assignment:
- Disallow assignment as an expression
- Use static type checking
- Assignment doesn't exist, we only have
let
bindings
- Allow an initialization statement, disallow assignment otherwise
- Disallow assignment inside a conditional without declaration
I've ranked them in order of ascending preference – assignments inside expressions can be useful (and it's simple to circumvent Python's problems by having an explicit declaration syntax, and a different named argument syntax). But it's ok to disallow them, as there are many other options to the same effect.
Bug-free code is more important than terse code.
while((x = getValue()) != null) {}
. Replacements will be uglier since you'll need to either usebreak
or repeat thex = getValue
assignment.