GoSuda

Confronto tra progettazione linguistica in contesti di generazione di codice tramite LLM: costo dell'astrazione ed esplicitezza di Go

By Rabbit Princess views ...

Linguaggi moderni

Per linguaggio moderno si intende un linguaggio che offre una produttività, una stabilità, una sicurezza e una manutenibilità superiori rispetto ai linguaggi del passato. Questi linguaggi adottano attivamente tecniche e concetti recenti e forniscono agli sviluppatori strumenti di sviluppo più efficienti e sicuri. Tra i linguaggi moderni più rappresentativi figurano Java, Rust, Python, TypeScript e altri, che presentano le seguenti caratteristiche.

1. Progettazione orientata agli oggetti ( OOP )

I linguaggi moderni sono progettati per lo più sulla base dei concetti di programmazione orientata agli oggetti. La programmazione orientata agli oggetti supporta principi quali incapsulamento, ereditarietà e polimorfismo e, suddividendo sistemi complessi in oggetti di dimensioni ridotte, rende la struttura del programma chiara e flessibile. Ciò risulta particolarmente efficace nello sviluppo di software su larga scala, poiché consente di controllare i costi e lo sforzo di sviluppo e di ridurre i problemi che possono emergere durante la manutenzione.

2. Zucchero sintattico ed espressioni

I linguaggi moderni offrono una varietà di zucchero sintattico e di costrutti basati su espressioni, allo scopo di migliorare la leggibilità del codice e la produttività dello sviluppo. Funzionalità come l'operatore ternario, le espressioni lambda e il pattern matching riducono il codice boilerplate, rendono il codice prevedibile e incrementano la produttività degli sviluppatori.

3. Sistema dei moduli

I linguaggi moderni consentono di suddividere e gestire un programma in più unità mediante un sistema dei moduli. Ciò facilita il riutilizzo del codice e la gestione delle dipendenze e rende agevole la manutenzione anche quando la dimensione del progetto cresce. Esempi rappresentativi includono Maven/Gradle per Java, Cargo per Rust e npm/yarn per TypeScript.

4. Linguaggi intermedi e macchine virtuali

I linguaggi moderni adottano linguaggi intermedi e macchine virtuali per garantire indipendenza dalla piattaforma, ottimizzazione delle prestazioni e sicurezza. Esempi rappresentativi sono JVM, LLVM, WASM, GraalVM e altri.

Lo sviluppo nell'era dell'IA e i limiti dei linguaggi moderni

I linguaggi moderni sono nati con la premessa che la maggior parte del codice fosse scritta dalle persone. Naturalmente, lo scopo dei linguaggi moderni è ridurre le operazioni ripetitive, aumentare la produttività e implementare in modo efficace software di grandi dimensioni attraverso strutture che consentono la collaborazione tra sviluppatori. Tuttavia, nell'era dell'IA tale premessa si sta progressivamente indebolendo. Strumenti basati sull'IA come Copilot e Cody automatizzano gran parte della scrittura del codice, e la quantità di codice che un singolo sviluppatore può produrre sta crescendo in modo straordinario. Di conseguenza, caratteristiche dei linguaggi moderni che in passato erano percepite come vantaggi stanno gradualmente trasformandosi in svantaggi obsoleti.

Progettazione orientata agli oggetti

L'IA è in grado di analizzare in modo rapido e accurato strutture in cui tutte le informazioni sono esplicitate all'interno di una singola funzione o modulo. Al contrario, all'aumentare del contesto cresce l'intervallo su cui deve essere svolta l'inferenza, e pertanto la produttività e l'accuratezza dell'IA diminuiscono. Il codice scritto secondo la OOP non gestisce la logica in un unico punto, ma la distribuisce tra più oggetti; di conseguenza, l'IA richiede un contesto maggiore. Si consideri il seguente codice.

 1public class AnimalHandler {
 2    public void handle(Animal animal) {
 3        animal.speak();
 4    }
 5}
 6
 7public class Main {
 8    public static void main(String[] args) {
 9        AnimalHandler handler = new AnimalHandler();
10
11        Animal a1 = new Dog();
12        Animal a2 = new Cat();
13        Animal a3 = new Horse();
14
15        handler.handle(a1);
16        handler.handle(a2);
17        handler.handle(a3);
18    }
19}

Nel codice precedente, affinché l'IA sappia quale comportamento venga effettivamente eseguito da speak(), deve ripetere il seguente ragionamento

  1. Di quale classe concreta è istanza animal?
  2. Dove è definito il metodo speak() sovrascritto in quella classe?
  3. Dove sono definite le classi Dog e Cat e quale comportamento contengono?
  4. Esiste la possibilità che le classi Dog e Cat siano sovrascritte in altre classi?

Queste informazioni non si trovano raccolte in un unico file o in una sola funzione, ma possono essere comprese solo seguendo le relazioni e la struttura di ereditarietà tra i file delle classi. Inoltre, gli elementi determinati a runtime, come la reflection o il caricamento dinamico, per l'IA costituiscono di fatto una scatola nera, rendendone l'analisi praticamente impossibile.

Zucchero sintattico ed espressioni

L'IA predilige il comportamento esplicito rispetto a quello implicito e preferisce combinare strutture semplici per ottenere un'unica soluzione corretta piuttosto che utilizzare strutture complesse. Lo zucchero sintattico, al contrario, consente molteplici forme di espressione, spesso con comportamenti interni identici o simili. L'IA deve apprendere individualmente il significato di queste diverse espressioni e può trovarsi in difficoltà nel determinare quale costrutto raccomandare, e con quale priorità, in una data situazione.

Linguaggi intermedi e macchine virtuali

L'IA apprende prevalentemente a partire dal codice sorgente. Il linguaggio intermedio (bytecode), invece, durante la compilazione perde informazioni semantiche come i nomi delle variabili e deve essere reinterpretato dalla macchina virtuale in fase di esecuzione. Si tratta di un'operazione molto difficile da comprendere o trasformare per l'IA. Ad esempio, per l'IA la conversione da TypeScript a Go è possibile, mentre convertire il bytecode di V8 engine in bytecode JVM o in codice macchina è praticamente impossibile.

Il vero linguaggio moderno nell'era dell'IA

Da quanto esposto emerge che le caratteristiche dei linguaggi moderni sono, in realtà, elementi obsoleti e poco adatti all'era dell'IA. Quale linguaggio potrebbe quindi essere il più adatto come linguaggio moderno per l'era dell'IA? Una risposta può essere ricavata dagli elementi che i linguaggi moderni del passato hanno rifiutato. Un linguaggio moderno per l'era dell'IA deve:

  1. Possedere una sintassi esplicita e un flusso naturale.
  2. Superare la struttura OOP eccessiva e avere una struttura piatta e semplice.
  3. Privilegiare un insieme limitato di parole chiave di base per realizzare più funzionalità, anziché zucchero sintattico superfluo o schemi impliciti.
  4. Avere un sistema di build semplice e prevedibile, anziché un sistema di build complesso.

Il linguaggio che soddisfa nel modo più completo questi criteri è Golang. Il linguaggio Go è stato progettato in modo che, con un numero minimo di parole chiave fondamentali e una combinazione semplice di costrutti sintattici, sia possibile implementare la maggior parte delle funzionalità pratiche, escludendo in modo rigoroso l'astrazione superflua e il comportamento implicito. Ciò rappresenta una struttura vantaggiosa non solo per gli sviluppatori, ma anche per l'IA nell'interpretazione e nella generazione del codice.

Come la struttura prevedibile e piatta, il flusso esplicito e la sintassi coerente a cui Go è orientato, a differenza dei linguaggi moderni tradizionali progettati sulla base degli sviluppatori umani, si adattano meglio a un ambiente di sviluppo in cui l'IA partecipa attivamente. La filosofia di progettazione conservatrice di Go, un tempo considerata obsoleta, si rivela così, nell'era dell'IA, la scelta più avanzata.