Molte lingue scelgono il percorso di assegnazione di un compito piuttosto che un'espressione, incluso Python:
foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg
e Golang:
var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies
Altre lingue non hanno assegnazioni, ma piuttosto associazioni con scope, ad es. OCaml:
let foo = 42 in
if foo = 42 then
print_string "hi"
Tuttavia, let
è un'espressione stessa.
Il vantaggio di consentire l'assegnazione è che possiamo controllare direttamente il valore di ritorno di una funzione all'interno del condizionale, ad es. in questo frammento di Perl:
if (my $result = some_computation()) {
say "We succeeded, and the result is $result";
}
else {
warn "Failed with $result";
}
Perl estende ulteriormente la dichiarazione solo a quella condizionale, cosa che lo rende molto utile. Ti avvertirà anche se assegni all'interno di un condizionale senza dichiarare una nuova variabile - if ($foo = $bar)
avviserà, if (my $foo = $bar)
non lo farà.
Rendere il compito in un'altra istruzione è di solito sufficiente, ma può portare problemi di scoping:
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 fa molto affidamento sui valori di ritorno per il controllo degli errori. Pertanto consente a un condizionale di prendere una dichiarazione di inizializzazione:
if result, err := some_computation(); err != nil {
fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)
Altre lingue usano un sistema di tipi per non consentire espressioni non booleane all'interno di un condizionale:
int foo;
if (foo = bar()) // Java does not like this
Ovviamente non funziona quando si utilizza una funzione che restituisce un valore booleano.
Ora abbiamo visto diversi meccanismi per difendersi dall'assegnazione accidentale:
- Non consentire il compito come espressione
- Usa controllo tipo statico
- L'assegnazione non esiste, abbiamo solo
let
binding
- Consenti una dichiarazione di inizializzazione, non consentire l'assegnazione altrimenti
- Non consentire il compito all'interno di un condizionale senza dichiarazione
Li ho classificati in ordine di preferenza ascendente - gli assegnamenti all'interno delle espressioni possono essere utili (ed è semplice aggirare i problemi di Python avendo una sintassi esplicativa delle dichiarazioni e una sintassi differente degli argomenti con nome). Ma è giusto disabilitarli, poiché ci sono molte altre opzioni per lo stesso effetto.
Il codice senza bug è più importante del codice tergente.