Ciao, e benvenuto nella quinta puntata della sessantottesima stagione. Finora ho dipinto un quadro ordinato: uno stato, un passo, una regola che dice come proseguire, e via, fino al risultato. È il caso bello. Ma non sempre le cose vanno lisce. Oggi arriva la puntata onesta, quella che guarda cosa succede quando l'esecuzione non arriva serena alla fine. E soprattutto cosa succede quando le regole, semplicemente, tacciono.
Partiamo dai tre destini possibili di un'esecuzione, perché sono solo tre. Il primo: il programma arriva a un valore e si ferma. Ha finito, con un risultato in mano. È il finale che speriamo. Il secondo: il programma continua a fare passi per sempre, senza mai arrivare a un valore. Un ciclo che non si chiude, un'attesa infinita, un server che resta in ascolto. Non è bloccato: è vivissimo, fa passi in continuazione, solo che non arriva mai a un valore finale, e a volte è proprio quello che vuoi. E poi c'è il terzo destino, il più interessante.
Vediamolo, il terzo destino: il programma si incastra. Arriva in una situazione da cui nessuna regola dice come proseguire. Non ha un valore in mano, quindi non ha finito. Ma non può nemmeno fare un altro passo, perché non esiste una regola che copra quella situazione. È fermo, in stallo, in un vicolo cieco. E qui c'è un'idea preziosa: questa, esattamente questa, è la definizione precisa di un errore a runtime. Un errore non è una cosa vaga e misteriosa. È una configurazione da cui il gioco non sa come continuare. Chiedere una cosa che non ha senso, e trovarsi in un punto dove il regolamento non prevede alcuna mossa.
Nota quanto è pulita questa definizione, perché ti dà una lente nuova sugli errori. Quando il tuo programma esplode a runtime, non è successo niente di soprannaturale: sei arrivato in uno stato incastrato. Hai chiesto un elemento che non c'è, hai usato qualcosa che non era pronto, hai trattato una cosa come se fosse un'altra. In tutti questi casi, il gioco guarda la situazione, cerca una regola che gli dica cosa fare, non la trova, e si ferma. L'errore è l'assenza di una mossa legale. Capire questo cambia il modo in cui guardi un crash: non un evento oscuro, ma un vicolo cieco preciso, in un punto preciso.
Ora arriva la parte davvero onesta, quella scomoda. Finora ho parlato come se, per ogni linguaggio, esistesse un regolamento completo, che copre ogni situazione senza buchi. La verità è che scrivere un regolamento del genere, per un linguaggio vero, è enormemente difficile. I linguaggi reali sono grandi, pieni di angoli, di casi limite. E in alcuni di quegli angoli, le regole non dicono nulla. Non nel senso che il programma si incastra in modo pulito: nel senso che il regolamento ufficiale, di proposito o per disattenzione, lascia quella situazione senza risposta. È un buco nella definizione del significato.
Ecco dove nasce quel fenomeno di cui parlammo la scorsa stagione: la terra di nessuno, il comportamento non definito. Quando finisci in una di quelle zone, il significato del tuo programma semplicemente non esiste. Le regole non lo coprono. E allora ogni implementazione fa come le pare: una si comporta in un modo, un'altra in un altro, la stessa magari cambia idea tra due versioni. Non c'è un giusto, perché non c'è una regola. Sapere dove il regolamento tace non è pedanteria accademica: è sapere esattamente in quali punti il tuo codice smette di avere un significato garantito, e diventa una scommessa.
C'è di più, ed è la confessione più grande. La maggior parte dei linguaggi che usi ogni giorno non ha affatto un regolamento formale, scritto con precisione matematica. Ha una descrizione a parole, in prosa, che come ogni testo in lingua umana può essere ambigua e interpretabile. E ha un'implementazione di riferimento, quella considerata la verità, quella per cui si dice: fa così perché così fa quel programma. La teoria pulita, con le sue regole precise, esiste per pochi linguaggi fortunati. Per tutti gli altri, il significato vero è un misto di prosa e di come si comporta in pratica lo strumento ufficiale.
Resta la lezione di fondo, ed è una lezione di umiltà utile. Il modello di stato e passo resta lo strumento giusto per pensare: solo, va usato sapendo che i confini sono più sfumati di quanto una teoria ordinata lasci credere. Il codice ben scritto vive dentro le zone dove le regole parlano chiaro, e sta alla larga dagli angoli dove tacciono. E la maturità, qui come altrove, non è fingere che tutto sia perfettamente definito: è sapere dove finisce il terreno solido, e non costruirci sopra la casa.
Per oggi ci fermiamo qui. Abbiamo guardato cosa succede quando l'esecuzione non fila liscia. I tre destini: arrivare a un valore e fermarsi, andare avanti per sempre senza finire, o incastrarsi in una situazione da cui nessuna regola dice come proseguire. Quel terzo caso, lo stallo, è la definizione precisa di un errore a runtime: l'assenza di una mossa legale. Poi la parte scomoda: i regolamenti reali hanno buchi dove le regole tacciono, ed è lì che nasce il comportamento non definito, dove il significato del programma smette di esistere. E la confessione: la maggior parte dei linguaggi non ha un regolamento formale, ma prosa e un'implementazione di riferimento. La lezione: sapere dove finisce il terreno solido, e non costruirci la casa. Nella prossima puntata: quando le regole lasciano scegliere. Nelle note trovi qualche spunto. Se ti è utile, condividila. Grazie per l'ascolto, e ci sentiamo alla prossima.