
Nim e Python, due linguaggi complementari (PyVenice #6)
Lo scorso 17 settembre, al PyVenice #6 a Verona ospitato al 311 Verona, ho tenuto un talk intitolato (volutamente un po’ clickbait) Nim e Python, con tanto di sondaggio iniziale: nessuno in sala conosceva Nim. In questo post provo a riassumerlo, con la stessa idea di fondo: non è un versus, ma il racconto di due linguaggi che si completano.
Il video completo della serata è su YouTube: PyVenice #6 a Verona. Slide e codice: codeberg.org/n1k9/py-venice-202606.
Non sono confrontabili (ed è proprio il punto)
Python è un linguaggio interpretato, uscito nel 1991. Nim è un linguaggio compilato, uscito nel 2008: più “maturo” di Rust (2012) o Zig (2016). Uno è pensato per un certo tipo di lavoro, l’altro per un tipo di lavoro diverso. Quello che li accomuna è la sintassi: Nim ha preso in prestito di proposito lo stile di Python per rendere friendly un linguaggio che, sotto, è a basso livello.
Quindi niente scontro: l’obiettivo è mostrare come si sovrappongono, e dove si integrano.
Compilato, non interpretato
Nim in realtà è transpilato: il compilatore traduce in C, C++, Objective-C o JavaScript, e poi quello viene compilato. Alla fine ottieni un eseguibile, senza dover distribuire un interprete.
Il vantaggio pratico:
- performance vicine al C compilato — nei casi mostrati, indicativamente da 5 a 20 volte più veloce di Python;
- distribuzione semplice: dai un binario e funziona, senza
pip installné dipendenze a runtime.
Il tutto mantenendo un garbage collector (con la possibilità di disabilitarlo e gestire la memoria a mano, utile per esempio nei videogiochi dove non puoi permetterti pause di allocazione).
Stessa forma, regole diverse
A prima occhiata il codice Python e quello Nim si somigliano molto, ma le differenze saltano fuori presto:
- Tipi ovunque: Nim è fortemente tipizzato, parametri e valori di ritorno vanno dichiarati (o inferiti dal compilatore);
let,var,const: si dichiarano esplicitamente le variabili.letè immutabile,varno,constè una sostituzione fatta in compilazione (come in C);- niente
returnobbligatorio: vale l’ultima espressione, oppure si usa la variabile implicitaresult, che funziona anche da accumulatore (a differenza direturn, che esce subito); ifspeculare: in Nim l’ifè un’espressione, quindi l’operatore ternario è la stessa cosa dell’if;- scope dei blocchi: una variabile ridichiarata dentro un
ifvive solo lì; usciti dal blocco torna quella di prima (in Python no).
Collezioni, funzioni e severità
Le liste di Python diventano in Nim le sequenze (seq), con una differenza importante: gli elementi devono essere tutti dello stesso tipo. È una scelta di performance — se il compilatore sa quanto occupa ogni elemento, accede molto più velocemente. Esistono anche gli array a lunghezza fissa, che in Python non ci sono.
Non ci sono le list comprehension né le f-string “native”, ma ci sono equivalenti: filter/map con funzioni anonime (i lambda, che in Nim si scrivono con func/proc senza nome) e il modulo fmt per l’interpolazione delle stringhe.
Sulle funzioni pure (func, senza effetti collaterali, in stile matematico) il compilatore può calcolare il risultato già in fase di compilazione. E sui tipi “nominali” Nim è severo: due int di domini diversi restano tipi diversi e non si possono mescolare — niente “pere e mele” sommate per sbaglio.
Un’altra differenza che spiazza chi viene da Python è la verità: niente valori truthy/falsy. Una lista vuota, lo zero o nil non sono “falsi”: devi confrontare esplicitamente. È più scomodo, ma aiuta a non sbagliare.
Metaprogrammazione: template e macro
Una delle parti più interessanti: in Nim puoi definire nuovi costrutti sintattici, cosa impossibile in Python.
- I template sono sostituzioni fatte in compilazione (come le
const): il codice viene copiato dove lo chiami, senza l’overhead di una chiamata di funzione, dello stack e del passaggio parametri. Perfetto per cose come un logger. - Le macro vanno oltre: modificano l’albero sintattico vero e proprio, generando codice su misura.
Il risultato è un eseguibile potenzialmente più lungo, ma molto più veloce in esecuzione — e noi non ce ne accorgiamo, perché vediamo solo il binario finale.
Niente classi, ma qualcosa di simile
Nim non ha oggetti nel senso della programmazione a oggetti: niente classi, niente ereditarietà. Ha object (simili a struct) e ref object (referenziati in memoria e modificabili), e degli enum.
Però c’è la uniform call syntax: una normale funzione il cui primo parametro è l’oggetto può essere chiamata come se fosse un metodo di quell’oggetto. È zucchero sintattico, ma permette di comporre chiamate in cascata e di scrivere codice che assomiglia molto al Python “a oggetti”. Si possono anche ridefinire gli operatori (+, ==, …) per i propri tipi.
E il punto vero: usarli insieme
Qui sta il cuore del discorso. Nim ha un ecosistema piccolo, è vero: niente Jupyter come si intende in Python, comunità ridotta. Ma ha un asso nella manica: l’interoperabilità con il C/C++/Objective-C. Puoi chiamare praticamente qualsiasi libreria scritta in quegli ecosistemi.
E non solo: puoi far parlare Nim e Python tra loro.
- Da Nim puoi importare Python (tramite librerie come
nimpy) e usare tutto ciò in cui Python eccelle: dati, AI, librerie. - Dal lato opposto, puoi compilare una funzione Nim (o C) come libreria condivisa e importarla in Python: i testi veloci e critici si scrivono in Nim, e Python li richiama in modo trasparente.
Hai un collo di bottiglia? Lo isoli, lo riscrivi in Nim, e lo usi da Python senza cambiare il resto del progetto. È la stessa strategia che altri adottano con Rust: pezzi caldi in un linguaggio compilato, il resto in Python.
Dove ha senso Nim (e dove no)
Con la possibilità di disabilitare il GC, Nim è comodo per videogiochi, per networking, per tool da riga di comando che vuoi distribuire come singolo eseguibile, per il frontend web (compila in JS) e soprattutto per l’embedded: viene usato per firmware di microcontrollori, un po’ come MicroPython.
Il cross-compiling e gli eseguibili autonomi lo rendono ideale quando non vuoi chiedere all’utente di installare un interprete. E se un domani cambiassi idea, non perdi nulla: il codice transpilato in C lo compili dove vuoi.
I limiti restano quelli di un linguaggio piccolo: meno librerie “pronte”, meno documentazione rispetto a Python. Ma con l’interoperabilità C e un package manager come Nimble, quel che manca spesso te lo costruisci — o lo prendi in prestito.
Il resto della serata
A seguire, Alessandra Bilardi ha confrontato performance e comportamento di assistenti AI open e closed: in un ecosistema sempre più affollato, quale scegliere e per quale scopo. Anche quella parte della serata merita una visione.
In conclusione
Nim non sostituisce Python, e non è nato per farlo. È un linguaggio stabile, con una curva di apprendimento dolcissima per chi viene da Python, che eccelle dove serve velocità e un binario autonomo. La cosa più interessante è usarli insieme: Python resta il posto giusto per orchestratare, Nim entra in gioco dove le performance contano davvero.
Se il tema vi incuriosisce, il video integrale è online e slide e codice sono su Codeberg. Grazie a PyVenice, a Python Italia, al 311 Verona e a chi è passato a sentirlo dal vivo.