Le basi
if in Zero somiglia più o meno all'if di ogni altro linguaggio della famiglia del C, con una restrizione importante: la condizione deve essere un bool.
La condizione value == 42 è un bool perché == tra due i32 restituisce un bool. I due rami contengono ciascuno un blocco di istruzioni. Uno dei rami si può in un certo senso omettere, perché l'else è facoltativo, ma quando ci sono entrambi devono essere blocchi { ... }.
Il requisito del bool
Alcuni linguaggi ti permettono di scrivere if value { ... } e trattano 0, "" o null come falsi. Zero no. La condizione deve essere un vero bool.
let count = 0
if count { // errore di compilazione: atteso bool, trovato i32
// ...
}
if count == 0 { // OK
check world.out.write("niente da fare\n")
}
Il motivo è lo stesso che attraversa tutto il linguaggio: l'esplicito batte l'implicito. Un agente che legge la seconda forma sa esattamente cosa viene controllato. La prima forma lascia implicite le regole di truthiness, che cambiano da un linguaggio all'altro: Zero sceglie di farne a meno.
Else ed else if
La clausola else viene eseguita quando la condizione è falsa:
Concatena le condizioni con else if:
if value < 0 {
check world.out.write("negativo\n")
} else if value == 0 {
check world.out.write("zero\n")
} else {
check world.out.write("positivo\n")
}
else if è semplicemente else { if ... } senza le parentesi graffe: stessa semantica, meno indentazione. L'else finale è facoltativo.
Da dove arriva la condizione
La condizione può essere qualsiasi cosa che produca un bool:
- Un binding
bool:if ok { ... }. - Un confronto:
if value == 42 { ... },if x < y { ... }. - Una combinazione logica:
if a && b { ... },if pair.left == 1_u8 && pair.right == 2_u8 { ... }. - Una chiamata a funzione che restituisce
bool:if isReady(world) { ... }.
Un frammento reale tratto dagli esempi del linguaggio:
let pair: BytePair = Pair { left: 1_u8, right: 2_u8 }
if pair.left == 1_u8 && pair.right == 2_u8 {
check world.out.write("type alias ok\n")
} else {
check world.out.write("type alias broke\n")
}
&& fa la valutazione in cortocircuito come ti aspetti: se il lato sinistro è false, il lato destro non viene valutato. Lo stesso vale per ||.
Quando usare invece match
Le catene if/else if vanno bene per decisioni a due o tre vie, soprattutto quando le condizioni non hanno una struttura in comune. Ma quando scegli in base alle varianti di un tipo somma, cioè una choice o un enum, lo strumento giusto è match.
Confronta:
// match: esaustivo, controllato dal compilatore
match result {
.ok => value { check world.out.write("ok\n") }
.err => message { check world.out.write("err\n") }
}
con:
// if/else su un tipo somma: prolisso e più facile da sbagliare
if result.isOk() {
let value = result.unwrap()
check world.out.write("ok\n")
} else {
check world.out.write("err\n")
}
La forma match rende esplicita l'analisi delle varianti, e il compilatore protesterà se più avanti viene aggiunta una variante e ti dimentichi di gestirla. La forma if fa passare in silenzio al ramo else tutto ciò che non è ok, perfino varianti nuove a cui non hai mai pensato.
Regola pratica: if per i booleani, match per le varianti.
Note di stile
Alcune piccole convenzioni che rendono il codice più facile da leggere sia per le persone sia per gli agenti:
- Metti sempre le parentesi graffe attorno al corpo. Anche un corpo di una sola riga va in
{ ... }. Non esiste una forma a istruzione singola senza graffe. È voluto: mantiene affidabile l'indentazione ed elimina una fonte comune di bug nella famiglia del C. - Metti per primo il ramo più comune. Chi legge (persona o agente) scorre dall'alto verso il basso.
- Preferisci le condizioni positive quando sono altrettanto chiare. A parità di tutto il resto,
if isReadyè più facile da seguire diif !notReady.
Prossimo passo: cicli while
Le condizioni decidono cosa fare; i cicli decidono quante volte farlo. La prossima pagina tratta i cicli while di Zero, il costrutto di ciclo con cui il linguaggio viene distribuito in questa prima fase.
Domande frequenti
Come funzionano le istruzioni if in Zero?
Usa if condition { ... } con una clausola else { ... } facoltativa. La condizione deve essere un bool: Zero non converte interi, stringhe o altri valori in booleani. Esempio: if value == 42 { check world.out.write("sì\n") } else { check world.out.write("no\n") }.
Zero ha un operatore ternario?
Zero mantiene di proposito una superficie piccola e usa blocchi if / else invece di un ternario separato condition ? a : b. Gli esempi ufficiali preferiscono rami espliciti anche per i casi brevi. Se il linguaggio supporterà if come espressione, aspettati che Zero vada in quella direzione invece di introdurre una seconda sintassi.
Si possono concatenare if/else if in Zero?
Sì. Concatena le condizioni scrivendo else if tra i rami: if a { ... } else if b { ... } else { ... }. Per scelte più elaborate su una choice o un enum, usa invece match: è esaustivo e il compilatore controlla che tu abbia gestito ogni variante.
Perché Zero non converte i valori in bool?
La filosofia di design di Zero è 'tutto è esplicito'. La truthiness implicita, cioè trattare 0, "" o null come falso, è una fonte frequente di bug ed è difficile da analizzare con precisione per un agente. Richiedere un vero bool rende visibile nel codice sorgente la decisione di diramazione e più facile da verificare in modo meccanico.
Quando conviene usare match invece di if/else?
Usa match quando scegli un ramo in base alle varianti di una choice o di un enum. Il compilatore può verificare che tu abbia coperto ogni caso, cosa che le catene if/else if non possono fare. Tieni if/else per i predicati booleani semplici: controlli di uguaglianza, confronti e brevi scelte a due vie.