Cos'è Lua e perché lo trovi dentro altri programmi
Cos'è Lua, perché nasce per essere incorporato dentro altri programmi, la table come unica struttura dati e le trappole per chi arriva da altri linguaggi.
Prima o poi lo incontri: vuoi scrivere un gioco su Roblox, configurare Neovim, fare uno script per un server, e scopri che il linguaggio è Lua. Non lo hai scelto tu, te lo ha imposto il programma che stai usando. In questo articolo trovi perché succede sempre così, e cos'è davvero Lua al di là della sintassi.
Cos'è Lua
Lua è un linguaggio di scripting minuscolo, nato in Brasile nel 1993, progettato non per scrivere applicazioni autonome ma per essere incorporato dentro un programma già esistente scritto in C, in modo che gli utenti possano estenderlo senza ricompilarlo.
Questa frase spiega ogni singola caratteristica del linguaggio, ed è il motivo per cui Lua sembra "povero" a chi lo giudica come se fosse un concorrente di Python. Non lo è. Non gioca nello stesso campionato e non ci ha mai provato.
Il nome significa "luna" in portoghese.
Il punto di partenza: essere ospite, non padrone
Immagina di avere un motore di gioco scritto in C++. È veloce, ma ogni volta che un designer vuole cambiare il comportamento di un nemico devi ricompilare mezz'ora di codice. La soluzione è incorporare un interprete dentro il motore: il designer scrive uno script, il motore lo esegue a caldo, nessuna ricompilazione.
Perché quell'interprete funzioni, deve avere tre proprietà non negoziabili:
- Essere piccolo. Non puoi infilare dentro il tuo eseguibile un runtime da centinaia di megabyte. L'intero interprete Lua sta in poche centinaia di kilobyte.
- Essere veloce ad avviarsi. Viene creato e distrutto continuamente, magari uno per ogni entità.
- Parlare con il C senza attriti. Il programma ospite deve poter chiamare funzioni Lua e Lua deve poter chiamare funzioni del programma ospite.
Lua è costruito attorno a questi tre vincoli. Ogni cosa che manca — e mancano molte cose — manca perché aggiungerla avrebbe rotto uno dei tre.
Il confronto utile è con Python, che è pensato per essere il programma principale e porta con sé una libreria standard enorme. Lua ha una libreria standard quasi imbarazzante nella sua essenzialità: niente rete, niente accesso avanzato al filesystem, niente date sofisticate. Tutto quello che serve lo fornisce il programma che lo ospita.
Dove lo incontri davvero
Non troverai quasi mai un annuncio di lavoro intitolato "sviluppatore Lua". Lo troverai dentro altre cose:
Videogiochi. È il caso d'uso storico e ancora dominante. Roblox lo usa (in una variante chiamata Luau) per tutta la logica di gioco, e questo da solo significa milioni di persone che scrivono Lua. World of Warcraft ha sempre usato Lua per le interfacce e gli addon. Love2D è un framework 2D interamente basato su Lua. Se stai valutando dove costruire un gioco, il quadro è in scegliere la tecnologia per un videogioco.
Neovim. L'editor ha adottato Lua come linguaggio di configurazione e plugin al posto del vecchio Vimscript. Chiunque personalizzi seriamente Neovim finisce a scrivere Lua, spesso senza averlo mai deciso.
Redis. Puoi inviare uno script Lua al database ed eseguirlo lato server in modo atomico. Serve quando devi fare più operazioni senza che nessun altro si infili in mezzo.
Nginx, tramite OpenResty. Permette di scrivere logica applicativa dentro il web server: autenticazione, instradamento, limitazione delle richieste, senza passare la richiesta a un backend separato.
Sistemi embedded e dispositivi. Su hardware con memoria contata, un interprete che pesa poche centinaia di kilobyte è spesso l'unico linguaggio di alto livello che ci sta.
Il filo comune: in nessuno di questi casi Lua è il protagonista. È lo strato con cui personalizzi qualcos'altro.
Una sola struttura dati: la table
Qui sta il concetto tecnico centrale, ed è quello che va capito bene perché regge tutto il resto.
In Lua esiste una sola struttura dati composita: la table. Non ci sono array, liste, dizionari, insiemi, oggetti, classi come cose separate. C'è la table, e tutto il resto si costruisce con quella.
La table è un array associativo: coppie chiave-valore. Se usi numeri interi come chiavi si comporta da array; se usi stringhe si comporta da dizionario; se ci metti dentro delle funzioni si comporta da oggetto.
-- come array
local frutti = {"mela", "pera", "kiwi"}
print(frutti[1]) --> mela
print(#frutti) --> 3
-- come dizionario
local utente = {
nome = "Anna",
eta = 30,
}
print(utente.nome) --> Anna
print(utente["nome"]) --> Anna, è la stessa cosa
-- mescolate: è sempre la stessa struttura
local misto = {10, 20, nome = "test"}
utente.nome è solo zucchero sintattico per utente["nome"]. Non esiste un concetto di "campo di un oggetto" distinto da "chiave di un dizionario".
Le metatable: dove nascono gli oggetti
Se non ci sono le classi, come si fa la programmazione a oggetti? Con le metatable: una table che descrive come un'altra table deve comportarsi in situazioni speciali.
La più importante è __index: dice dove cercare una chiave quando nella table non c'è.
local Animale = {}
Animale.__index = Animale
function Animale.nuovo(nome)
local self = setmetatable({}, Animale)
self.nome = nome
return self
end
function Animale:parla()
print(self.nome .. " fa un verso")
end
local a = Animale.nuovo("Fido")
a:parla() --> Fido fa un verso
Quando chiami a:parla(), Lua cerca parla dentro a, non lo trova, guarda la metatable, trova __index = Animale, cerca lì e lo trova. Questa è l'ereditarietà: una catena di ricerca, costruita a mano.
Per l'ereditarietà tra "classi" si allunga la stessa catena:
local Cane = setmetatable({}, {__index = Animale})
Cane.__index = Cane
function Cane:parla()
print(self.nome .. " abbaia")
end
I due punti a:parla() sono l'altra convenzione da conoscere: equivalgono a a.parla(a), cioè passano automaticamente la table come primo argomento, che dentro il metodo si chiama self.
Il risultato è che in Lua il sistema a oggetti non è nel linguaggio, è una libreria che scrivi tu (o che trovi già scritta). È tipico della filosofia: il linguaggio fornisce un meccanismo, non una politica.
Le trappole per chi arriva da altri linguaggi
Due cose fanno perdere ore a tutti, e vanno sapute prima di scrivere la prima riga.
Gli indici partono da 1, non da 0
local a = {"x", "y", "z"}
print(a[1]) --> x
print(a[0]) --> nil
Non è un errore di progettazione né una bizzarria gratuita: Lua nasce come linguaggio di configurazione per persone che non erano programmatori, e per loro il primo elemento è il numero uno. Il problema è che quasi tutti gli altri linguaggi che incontrerai — JavaScript, Python, C, Java — partono da zero, e passare avanti e indietro genera errori di un'unità nei cicli per settimane.
Attenzione anche al conteggio: #tabella restituisce la lunghezza solo per table usate come array puri e senza buchi. Se ci sono chiavi non numeriche o un nil in mezzo, il valore non è affidabile.
Le variabili sono globali di default
Questa è peggiore, perché non produce un errore: produce bug silenziosi.
function calcola()
risultato = 42 -- GLOBALE, visibile a tutto il programma
end
function calcolaBene()
local risultato = 42 -- locale, come ti aspetti
end
Se dimentichi local, la variabile finisce nell'ambiente globale. In uno script piccolo non te ne accorgi. In un plugin caricato insieme ad altri venti plugin, due contatori chiamati i si sovrascrivono a vicenda e passi il pomeriggio a capire perché il ciclo finisce prima.
La regola pratica: scrivi local davanti a ogni variabile, sempre, a meno che tu non voglia esplicitamente il contrario. Vale anche per le funzioni.
Come contorno, un paio di differenze minori ma fastidiose:
| Cosa | In Lua |
|---|---|
| Diverso da | ~= e non != |
| Concatenare stringhe | .. e non + |
| Falso logico | Solo false e nil: 0 e "" sono veri |
| Commenti | -- per una riga, --[[ ]] per più righe |
| Fine blocco | end, niente parentesi graffe |
Quel "0 è vero" merita un secondo di attenzione: se arrivi da C o da JavaScript, if contatore then sarà vero anche quando il contatore è zero.
Conviene impararlo?
Risposta onesta: non lo impari "per lavoro" in senso generale, lo impari perché ti serve in un contesto specifico.
Non esiste un percorso di carriera "sviluppatore Lua" come esiste per Java o JavaScript. Esistono lavori in cui Lua è una delle competenze richieste, quasi sempre nel gaming o nell'infrastruttura, e in quei casi è una competenza di contorno accanto a C++ o a un altro linguaggio principale.
Ha senso studiarlo se:
- stai costruendo o modificando giochi su piattaforme che lo usano;
- personalizzi Neovim seriamente;
- lavori su nginx/OpenResty o su Redis con script lato server;
- sviluppi su hardware con risorse limitate.
Non ha senso studiarlo se:
- stai cercando il tuo primo linguaggio (vedi imparare a programmare da zero);
- vuoi costruire applicazioni web, mobile o desktop autonome;
- ti serve un ecosistema ricco di librerie pronte.
La buona notizia: Lua si impara in un weekend. Il linguaggio è così piccolo che il manuale di riferimento completo è più corto della documentazione di una singola libreria Python. Il tempo vero lo spendi a imparare l'API del programma che lo ospita — quella di Roblox, di Neovim, del motore di gioco — e quella è specifica e non trasferibile.
In sintesi
Lua è un linguaggio ospite, non un linguaggio padrone. Nasce per essere incorporato dentro un programma scritto in C, e ogni sua caratteristica — interprete minuscolo, sintassi ridotta, libreria standard essenziale, integrazione col C — discende da quel vincolo.
La table è l'unica struttura dati, e questo è il concetto da padroneggiare. Fa da array, dizionario e oggetto a seconda di come la usi, e con le metatable ci costruisci sopra l'ereditarietà, che nel linguaggio non esiste come costrutto nativo.
Le due trappole sono gli indici che partono da 1 e le variabili globali di default. La prima produce errori di un'unità, la seconda produce bug silenziosi che emergono solo quando il tuo codice convive con quello di altri: scrivi sempre local.
Lo impari quando ti serve, non prima. Non è un investimento di carriera a sé stante e non ha senso come primo linguaggio, ma è talmente piccolo che quando il contesto lo richiede il costo di apprendimento è basso. Per collocarlo rispetto agli altri c'è tutti i linguaggi di programmazione.
Se prima di Lua ti manca ancora la base — cicli, funzioni, strutture dati — è da lì che conviene partire: trovi i percorsi completi nei corsi di CodeGrind.
Contenuto redatto con l'assistenza di strumenti di intelligenza artificiale e rivisto dalla redazione di Codegrind, che ne è responsabile. Hai trovato un errore? Scrivici.