Muchos idiomas eligen la ruta para hacer que la asignación sea una declaración en lugar de una expresión, incluido Python:
foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg
y Golang:
var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies
Otros idiomas no tienen asignación, sino enlaces de ámbito, por ejemplo, OCaml:
let foo = 42 in
if foo = 42 then
print_string "hi"
Sin embargo, let
es una expresión en sí misma.
La ventaja de permitir la asignación es que podemos verificar directamente el valor de retorno de una función dentro del condicional, por ejemplo, en este fragmento de Perl:
if (my $result = some_computation()) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
Perl también incluye la declaración a ese condicional solamente, lo que lo hace muy útil. También advertirá si asigna dentro de un condicional sin declarar una nueva variable allí; if ($foo = $bar)
advertirá, if (my $foo = $bar)
no lo hará.
Hacer la asignación en otra declaración suele ser suficiente, pero puede traer problemas de alcance:
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 depende en gran medida de los valores de retorno para la comprobación de errores. Por lo tanto, permite que un condicional tome una declaración de inicialización:
if result, err := some_computation(); err != nil {
fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)
Otros lenguajes usan un sistema de tipos para no permitir expresiones no booleanas dentro de un condicional:
int foo;
if (foo = bar()) // Java does not like this
Por supuesto, eso falla cuando se usa una función que devuelve un valor booleano.
Ahora hemos visto diferentes mecanismos para defenderse contra la asignación accidental:
- No permitir la asignación como expresión
- Usar comprobación de tipo estático
- La asignación no existe, solo tenemos
let
enlaces
- Permitir una declaración de inicialización, no permitir la asignación de lo contrario
- No permitir la asignación dentro de un condicional sin declaración
Los he clasificado en orden de preferencia ascendente: las asignaciones dentro de las expresiones pueden ser útiles (y es simple evitar los problemas de Python al tener una sintaxis de declaración explícita y una sintaxis de argumento con nombre diferente). Pero está bien no permitirlos, ya que hay muchas otras opciones para el mismo efecto.
El código sin errores es más importante que el código conciso.