1. Introduzione
1.1 Panoramica
MALDA: The AI-First Programming Language è un linguaggio di programmazione moderno e orientato agli oggetti, pensato per costruire agenti AI e workflow di automazione. Con supporto nativo all'integrazione LLM, all'orchestrazione di agenti e ai sistemi multi-agente, MALDA rende semplice creare applicazioni intelligenti e autonome.
Il linguaggio ha una sintassi pulita e leggibile, le strutture di controllo essenziali, capacità di I/O built-in e pieno supporto alla programmazione orientata agli oggetti: classi, ereditarietà e polimorfismo. Ciò che distingue MALDA sono le capacità AI di prima classe - classi built-in per client LLM, conversazioni, agenti e tool, con cui si costruiscono applicazioni AI sofisticate con poco codice.
L'intestazione della pagina riporta la versione della toolchain distribuita (lo stesso numero della CLI). La semantica del linguaggio core è specificata in malda-language-1.0.
prompt + schema + validate, senza API key). Se stai imparando a programmare, impara prima il linguaggio core: io.print, variabili, input, condizionali, cicli e funzioni. Dopo il percorso core, diramati verso Testing e qualità, Dati e database e App nel browser. Entrambi i percorsi arrivano allo stesso runtime AI-first.
Che cosa significa MALDA?
MALDA: Multi Agent Language with Development Automation - Il nome MALDA riflette i principi e la filosofia di progettazione del linguaggio:
- Multi Agent: supporto nativo all'orchestrazione di più agenti AI che lavorano insieme. MALDA offre classi built-in e supporto a runtime per creare, coordinare e gestire più agenti autonomi che collaborano su compiti complessi, condividono il contesto e operano in sistemi gerarchici.
- Language: un linguaggio di programmazione completo, con sintassi, runtime e tooling. MALDA non è solo un framework o una libreria: è un linguaggio a tutti gli effetti, con sintassi propria, interprete, compilatore, Desktop IDE e strumenti di sviluppo.
- Development: una toolchain per costruire e distribuire software reale, non solo per scriptare uno strato AI sopra un altro stack. L'interprete, il compilatore verso eseguibili standalone e JavaScript, gli IDE, il language server e il supporto built-in a test e database coprono l'intero ciclo di build.
- Automation: automazione di quel lavoro di sviluppo, in entrambe le direzioni. Gli agenti di coding possono scrivere MALDA: il language pack compatto in
docs/llm/insegna la sintassi a un modello che non l'ha mai vista, e le diagnostiche del language server chiudono il ciclo di feedback - MALDA stesso è stato costruito in larga parte con agenti di coding, e i due showcase più grandi di questo repository (Examples/Agents/secondbrain_semantic.maldaeExamples/RalphWiggum/) sono stati scritti interamente da agenti di coding. Nell'altra direzione, i programmi MALDA automatizzano a loro volta il lavoro di sviluppo, perché prompt, tool, agenti, endpoint e workflow durevoli sono costrutti del linguaggio e non chiamate di libreria: un programma può leggere una specifica, generare codice, eseguire i test e distribuire il risultato.
Il runtime unificato che rende possibile tutto questo è descritto di seguito.
1.2 Architettura AI Unified Runtime
MALDA poggia sull'AI Unified Runtime Architecture - un sistema unificato in cui il runtime del linguaggio, le capacità AI, il framework degli agenti, l'ecosistema di tool e il deployment sono integrati in un'unica architettura coerente. A differenza degli approcci tradizionali che combinano librerie e framework separati, MALDA offre un runtime unificato in cui tutto collabora senza interruzioni.
Che cos'è l'AI Unified Runtime Architecture?
AI Unified Runtime Architecture significa che le capacità AI non sono aggiunte come librerie o framework esterni, ma sono costruite direttamente nel runtime del linguaggio. Ne risulta un sistema unificato in cui:
- Language Runtime: l'interprete esegue il codice e gestisce lo stato del programma
- AI Runtime: client LLM, conversazioni, agenti e tool girano nello stesso processo
- Orchestrazione di agenti: più agenti si coordinano nello stesso ambiente di runtime
- Ecosistema di tool: i tool fanno parte del runtime, non sono plugin esterni
- Deployment Runtime: gli eseguibili compilati incorporano il runtime MALDA e le capacità AI. Alcune dipendenze esterne (es. librerie client SQL Server/PostgreSQL, DLL native LLamaSharp) possono richiedere DLL aggiuntive accanto all'eseguibile
Approccio tradizionale e runtime unificato
Negli approcci tradizionali, costruire applicazioni AI richiede di combinare più componenti separati:
- Runtime del linguaggio (es. interprete Python)
- Libreria di framework AI (es. LangChain)
- SDK del client LLM (es. OpenAI SDK)
- Sistema di tool personalizzato
- Sistema di deployment/packaging separato
Questo introduce overhead dalle chiamate di libreria, serializzazione tra i componenti e requisiti di setup complessi.
Con l'AI Unified Runtime Architecture tutti questi componenti sono integrati in un unico runtime:
- Nessun setup di framework: le capacità AI sono native del linguaggio
- Nessun overhead di libreria: esecuzione diretta nel runtime
- Gestione degli errori e type safety unificate su tutte le operazioni
- Deployment in un solo eseguibile: runtime MALDA e capacità AI incorporati. Alcune dipendenze esterne possono richiedere DLL aggiuntive
Componenti core
Runtime unificato del linguaggio
L'interprete include le capacità AI come funzionalità di prima classe. Classi come LLMClient, Agent e Conversation sono built-in nel linguaggio, non importate da librerie esterne.
Layer AI Runtime integrato
Le operazioni AI si eseguono nello stesso runtime del codice ordinario. Questo offre:
- Accesso diretto alla memoria, senza overhead di serializzazione
- Sistema di tipi unificato: gli stessi tipi per il linguaggio e per le operazioni AI
- Supporto all'esecuzione sincrona e asincrona
- Gestione degli errori integrata
Runtime unificato di orchestrazione degli agenti
Più agenti girano nello stesso processo e possono:
- Condividere spazio di memoria e contesto
- Comunicare direttamente, senza protocolli esterni
- Accedere allo stesso ecosistema di tool
- Coordinare l'esecuzione in modo efficiente
Runtime unificato dell'ecosistema di tool
I tool sono integrati nel runtime:
- I tool built-in (operazioni su file, ecc.) fanno parte del runtime
- I tool personalizzati creati con il decoratore
@Toolvengono registrati automaticamente - L'esecuzione dei tool avviene in-process con il runtime del linguaggio
- Non serve discovery o registrazione esterna dei tool
Runtime unificato di deployment
Il compilatore incorpora il runtime MALDA e le capacità AI in eseguibili standalone:
- Modalità Interpreter: incorpora interprete + codice sorgente + AI runtime
- Modalità Transpiler: fa transpile verso C# con integrazione nativa dell'AI runtime
- Risultato: un singolo file .exe con il runtime MALDA incorporato. Alcune dipendenze esterne (es. librerie client SQL Server/PostgreSQL, DLL native LLamaSharp) possono richiedere DLL aggiuntive da distribuire accanto all'eseguibile
Vantaggi
- Prestazioni: nessun overhead di chiamata di libreria, accesso diretto alla memoria, percorsi di esecuzione ottimizzati
- Semplicità: nessun setup di framework, sintassi nativa, meno dipendenze, meno boilerplate
- Type safety: gestione degli errori unificata, controllo dei tipi sulle operazioni AI, validazione a compile-time
- Esperienza di sviluppo: un solo IDE per linguaggio e AI, debugging integrato, documentazione unificata
- Deployment: un solo eseguibile con runtime MALDA e AI incorporati. Alcune dipendenze esterne (es. librerie client di database) possono richiedere DLL aggiuntive, ma la maggior parte delle applicazioni si distribuisce come file singolo
Come MALDA la implementa
L'implementazione MALDA dell'AI Unified Runtime Architecture è composta da quattro layer:
- Layer 1: Core Runtime - Lexer, Parser, AST, RuntimeValue, Environment, Execution Engine
- Layer 2: AI Runtime - LLMClientInstance, Conversation, AgentInstance, ToolInstance e classi di agenti specializzati
- Layer 3: Integration Layer - Language Service, AI Chat Service, Debugger, Compiler (tutti unificati)
- Layer 4: Deployment Runtime - compilatore che incorpora il runtime MALDA e le capacità AI (modalità interpreter) oppure fa transpile con integrazione nativa (modalità transpiler). Alcune dipendenze esterne possono richiedere DLL aggiuntive
Questa architettura rende MALDA davvero "AI-first": non un linguaggio con funzionalità AI aggiunte, ma un linguaggio in cui l'AI è fondamentale per l'architettura del runtime stesso.
1.3 Funzionalità del linguaggio
Capacità del linguaggio core
MALDA include:
- Variabili e memorizzazione dei dati
- Operazioni aritmetiche e logiche
- Diramazione condizionale (if/else)
- Cicli (while, for)
- Funzioni/procedure
- Template di prompt nominati (
prompt) - Operazioni di input/output
- Supporto alla ricorsione
- Programmazione orientata agli oggetti (classi, oggetti, ereditarietà)
- Orchestrazione di agenti AI: supporto nativo all'integrazione LLM, ai sistemi multi-agente e all'automazione intelligente
Funzionalità del linguaggio core
- Typing dinamico: le variabili possono contenere valori di qualsiasi tipo
- Type coercion: conversione automatica tra tipi compatibili (int ↔ float)
- Block scoping: le variabili sono in scope dei blocchi, con shadowing corretto
- Array: array dinamici con metodi built-in (append, pop, shift, concat)
- String interpolation: sintassi
$"..."per espressioni incorporate - Ricorsione: pieno supporto alle chiamate di funzione ricorsive
- Ereditarietà: ereditarietà singola con override dei metodi
- Membri static: campi e metodi a livello di classe
Funzionalità core AI-First
- Client LLM: classe
LLMClientper l'integrazione con API compatibili OpenAI (OpenAI, OpenRouter, LMStudio, OLLAMA, ecc.) - LLM locale di default: se non si passa un client, agenti e prompt usano il modello locale Qwen/Qwen2.5-0.5B-Instruct (scaricato come build GGUF da Hugging Face al primo uso) — nessuna API key
- LLM locali: classe
LlamaCppClientper l'inferenza locale con LLAMA.cpp e modelli GGUF - Client OpenRouter: classe
OpenRouterClientper modelli remoti con configurazione automatica della API key - Gestione delle conversazioni: classe
Conversationcon gestione automatica delle tool call ed esecuzione parallela dei tool in sola lettura (attiva di default) - Sistema di tool: classe
Toolcon supporto al formato function calling di OpenAI - Template di prompt: dichiarazioni
promptnominate con interpolazione dei parametri,awaitvalidato sullo schema e tool Mode B/C — vedi 10. Prompt - Tool personalizzati: decoratore
@Toolper definire tool LLM personalizzati a partire da funzioni MALDA - Classe Agent: classe
Agentper agenti autonomi con ruoli e istruzioni - Agenti specializzati:
CodingAgent,MALDACodingAgent,GitAgenteDevAgentcon tool preconfigurati - Orchestrazione multi-agente: supporto a più agenti che lavorano insieme, anche in sistemi gerarchici con
addSubAgent - Dashboard degli agenti: report automatico dello stato a una dashboard centrale (senza modifiche allo script MALDA)
- Sicurezza della working directory: validazione dei path per i tool di operazioni su file
⚠️ Importante: i sistemi di agenti autonomi con accesso ai tool comportano rischi significativi di sicurezza e operativi. Vedi 18. Orchestrazione di agenti per un disclaimer dettagliato e le strategie di mitigazione del rischio prima di distribuire agenti in produzione.
Funzionalità Web e API
- Supporto database: classi
SqlServerClient,PostgresClienteSqliteClientper operazioni su database con query parametrizzate e API query builder - Web server: classe
HttpServerper servire HTML - REST API: classe
RestServercon routing a decoratori (@GET,@POST, ecc.) - Framework UI Web:
@PAGEe@AIPAGEcon libreria di componenti per applicazioni full-stack con backend MALDA e UI renderizzata sul server - Server MCP: classe
MCPServerper esporre funzioni MALDA come tool MCP - Generazione UI: funzione
generateUI()e classeHTMLCacheper HTML generato da LLM - Server LLM: classe
LLMServerper creare server REST API LLM
1.4 Ambienti di sviluppo
Desktop IDE WPF
- Applicazione Windows nativa basata su AvalonEdit
- Syntax highlighting
- IntelliSense/auto-completamento
- Debugger completo con breakpoint, esecuzione passo-passo, watch delle variabili, call stack
- Diagnostica degli errori con report in tempo reale
- Integrazione del compilatore
- Browser e downloader dei modelli
- Configurazione di server MCP esterni
- Tab virtuali opt-in per un singolo file fisico
.malda, abilitate con i separatori// @malda-section Name - Coesiste con la composizione classica multi-file tramite
include(i file inclusi restano file fisici normali)
1.5 Compilatore
Il compilatore MALDA supporta la compilazione in due modalità:
- Modalità Interpreter: incorpora il codice sorgente e usa l'interprete a runtime
- Modalità Transpiler: fa transpile di MALDA verso C#, esecuzione nativa
Entrambe le modalità producono file .exe standalone con il runtime MALDA incorporato. Alcune dipendenze esterne (es. librerie client SQL Server/PostgreSQL, DLL native LLamaSharp) possono richiedere DLL aggiuntive accanto all'eseguibile.
1.6 Composizione del sorgente: include, using, import, export
MALDA ha tre modi per portare codice da altrove, e uno per controllare cosa un file offre agli altri. Tutti e quattro sono solo a top-level.
| Forma | Cosa fa |
|---|---|
include "path/file.malda"; | Compone il sorgente: carica e fa il parse di un altro file MALDA nel programma corrente, come se il suo testo ne facesse parte. |
using PackageName; | Carica un modulo di pacchetto installato e unisce i suoi simboli esportati nello scope corrente. |
import "path/file.malda"; | Carica un file locale come modulo e unisce solo i suoi simboli esportati. |
import { a, b } from "path/file.malda"; | Stesso caricamento, ma unisce solo gli export nominati nello scope corrente. |
import alias = ...; | Come sopra, ma lega i simboli esportati a un unico oggetto namespace invece che allo scope corrente. |
export function/class/var/type/schema | Marca una dichiarazione come parte della superficie pubblica di questo file. |
Quando usare ciascuno
- Usa
includequando stai semplicemente spezzando un programma su più file e vuoi che tutto sia visibile ovunque. - Usa
importquando un file è un'unità riutilizzabile con una superficie pubblica deliberata, soprattutto se vuoi i suoi simboli dietro un namespace. - Usa
usingper librerie riutilizzabili esterne installate come pacchetti.
export e la superficie di default
export può prefissare una dichiarazione di funzione, classe, var, type o schema:
// math_utils.malda
export function add(a, b) {
return a + b;
}
export var VERSION = "1.0";
export type Result = Ok(value) | Err(msg);
export schema Contact { name: string; }
function internalHelper(x) { // not exported
return x * 2;
}
export. Un modulo senza alcun export pubblica ogni simbolo top-level (inclusi type / schema). Aggiungere il primo export a un file esistente quindi nasconde tutto ciò che non hai marcato — usa export type / export schema quando il modulo esporta già dei valori.
Import nello scope corrente o dietro un namespace
Senza alias, i simboli esportati vengono uniti nello scope corrente. Un nome che esiste già non viene sovrascritto, quindi il file che importa vince sempre in caso di collisione:
import "shared/math_utils.malda";
print(add(2, 3)); // 5
print(VERSION); // "1.0"
// internalHelper(2); // not available: never exported
L'import selettivo estrae un sottoinsieme di quella superficie (i nomi mancanti o non esportati sono errori). Golden offline: Examples/Modules/selective_import.malda, Examples/Modules/export_type_schema.malda — vedi anche docs/selective-imports.md.
import { add, VERSION } from "shared/math_utils.malda";
print(add(2, 3));
print(VERSION);
// unused(); // not imported, even if exported by the module
import { Result, Contact } from "types_lib.malda";
var r = Ok(42); // constructors arrive with export type Result
var ok = validate("Contact", dict { "name": "Ada" });
Con un alias, gli stessi simboli sono raggruppati in un oggetto, il che evita del tutto le collisioni:
import mathUtils = "shared/math_utils.malda";
print(mathUtils.add(2, 3)); // 5
print(mathUtils.VERSION); // "1.0"
Le stesse due forme valgono per i pacchetti, con un nome di pacchetto puntato al posto del path stringa:
import App.Core;
import UI = App.Web.Components;
I nomi di pacchetto che iniziano con System, Microsoft o Windows vengono risolti come namespace .NET attraverso il layer di interop, non dal gestore di pacchetti. Vedi 30. Interop .NET.
Se non si riesce a risolvere un target è un errore a runtime: Package or module not found: Name per i pacchetti, e il messaggio di file-not-found sottostante per gli import di file.
Path e comportamento di sicurezza per include
includeè consentito solo a scope top-level (non dentro blocchi comeif,whileo corpi di funzione).- I path relativi di include si risolvono dalla directory del file che contiene l'istruzione include.
- Sono supportati anche i path assoluti.
- Le catene di include circolari vengono rifiutate con un errore di parse.
Nel Desktop IDE i tab virtuali non sostituiscono include: sono una vista dell'editor su un unico file fisico. Vengono creati solo quando il sorgente contiene i separatori espliciti // @malda-section Name. La risoluzione degli include resta basata sui file e funziona come prima.
// Local file composition: everything from these files becomes part of this program
include "shared/math_utils.malda";
include "features/orders.malda";
// Package import into the current scope
using App.Core;
// Package import behind a namespace object
using UI = App.Web.Components;
1.7 Esempio rapido
Prima i costrutti caratteristici (stesso programma di Examples/Basics/first_look.malda). -> Review lega il prompt allo schema. Senza await, la chiamata è un template reso — nessuna API key. validate("Review", …) è lo stesso controllo che await eseguirebbe sul JSON del modello.
schema Review {
summary: string;
issues: string[];
}
prompt codeReview(code, language) -> Review {
system: "You are an expert reviewer of {language}.",
user: "Review this {language} code:\n\n{code}"
}
var rendered = codeReview("function add(a, b) { return a + b; }", "javascript");
io.print(rendered.user);
var checked = validate("Review", {
"summary": "Looks fine",
"issues": []
});
if (checked.ok) {
io.print("schema ok: " + checked.data.summary);
}
La sintassi core usa le stesse istruzioni. Preferisci le chiamate namespaced (io.print); il print piatto continua a funzionare. I/O di file, path e ambiente: 12. Input/Output.
io.print("Hello, World!");
var name = "Alice";
var age = 25;
var pi = 3.14159;
var isActive = true;
var numbers = [1, 2, 3, 4, 5];
numbers.append(6);
function greet(name) {
return "Hello, " + name + "!";
}
io.print(greet("Bob"));
class Person {
public var name;
public var age;
function Person(name, age) {
this.name = name;
this.age = age;
}
public function introduce() {
io.print($"Hi, I'm {this.name} and I'm {this.age} years old.");
}
}
var person = new Person("Charlie", 30);
person.introduce();
Vedi anche
- 3. Struttura lessicale - Le basi della sintassi
- 4. Tipi di dati - Il sistema di tipi di MALDA
- 33. Esempi - Altri esempi completi