onto-rs
Implementazione Rust del linguaggio ONTO (Programmazione Orientata alle
Proprietà, logica trivalente V/F/N) con motore database unificato: il
linguaggio e il database sono lo stesso sistema — eseguire uno script
.onto popola direttamente un motore trivalente indicizzato
(OntoEngine), interrogabile nello stesso script.
Il paradigma è puro mereologico:
- Elementi = aggregati liberi di proprietà, senza schemi né classi
dichiarate a priori:
[ V=febbre, "Mario"=nome ] = mario. - Classi = aggregati intensionali lazy:
class { V=febbre ^ V=tosse } = Febbricitanti. La membership non si dichiara, si calcola sui bitset a ogni interrogazione; i nuovi elementi entrano nelle classi automaticamente;class fixedcongela uno snapshot. - Trivalenza = tre valori di verità, V/F/N, con le tavole della logica
ontologica: negazione asimmetrica, congiunzione come compressione
(
V ^ N = F,F ^ N = N), implicazione=>, coimplicazione<=>,xor. Un valore indeterminato (N) entra nel ragionamento invece di essere silenziato da un default booleano (vedidocs/LOGICA.md). La negazione stretta~isola, quando serve, una porzione che si comporta come la logica classica.
Quando usarlo
ONTO eccelle quando i dati sono profili booleani con valori mancanti che
contano, le query sono congiunzioni di proprietà e ranking per punteggio, e
il deployment è un processo Rust embeddato. Per tutto il resto esistono
strumenti migliori: la guida alla scelta, con diagramma decisionale, use case
concreti e limiti onesti, è in
docs/USE_CASES.md.
Documentazione
docs/LOGICA.md— logica ontologica: tavole canoniche, identità epistemica, leggi a confronto con la logica booleana, proprietà metalogichedocs/LEGGI_LOGICHE.md— derivazione computazionale delle leggi (generato dastrumenti/leggi_logiche.py)docs/METALOGICA.md— correttezza, completezza, decidibilità, conservatività, adeguatezza funzionale e struttura modale (generato dastrumenti/metalogica.py)docs/LINGUAGGIO.md— riferimento del linguaggiodocs/ARCHITETTURA.md— design di motore e interpretedocs/BENCHMARK.md— benchmark comparativo vs SQLite/sled/scan naivedocs/USE_CASES.md— guida alla scelta: quando usare ONTO e quando no
Quick start
# Build e test
cargo build --release
cargo test
# Esegue uno script
cargo run -p onto-cli -- run examples/diagnosi.onto
# REPL interattiva
cargo run -p onto-cli -- repl
# Persistenza: salva lo stato del motore su file e lo ricarica in un run successivo
cargo run -p onto-cli -- run examples/diagnosi.onto --save stato.bin
cargo run -p onto-cli -- run examples/negozio.onto --load stato.bin --save stato.bin
# nella REPL: ':save stato.bin' e ':load stato.bin'
# Stato persistente con log di scrittura: ripristinato all'avvio, ogni
# elemento e relazione registrati prima di essere applicati, checkpoint a fine run;
# rieseguire lo stesso script non reinserisce i suoi elementi
cargo run -p onto-cli -- run examples/diagnosi.onto --store dati/
cargo run -p onto-cli -- repl --store dati/ # ':checkpoint' compatta il log
# Servizio di rete: ogni POST esegue il sorgente ONTO nel corpo della richiesta
cargo run -p onto-cli -- serve --port 7878 --store dati/ --workers 4
curl -X POST http://127.0.0.1:7878/ --data-binary '[ "Mario"=nome, V=febbre ] = mario'
curl -X POST http://127.0.0.1:7878/leggi --data-binary 'top_k{ 5=k } = t : list
"{t}" : print'
# Con cifratura e token
cargo run -p onto-cli -- serve --tls-cert cert.pem --tls-key chiave.pem --token segreto
# Gruppo di tre nodi con sostituzione automatica del primario
cargo run -p onto-cli -- serve --port 7001 --store n1/ --node host1:7001 --replica host2:7002 --replica host3:7003
cargo run -p onto-cli -- serve --port 7002 --store n2/ --node host2:7002 --primary host1:7001 --replica host1:7001 --replica host3:7003
# Dati ripartiti tra due gruppi: la chiave sceglie il gruppo
cargo run -p onto-cli -- route --port 7800 --group host1:7001,host2:7002,host3:7003 --group host4:7001
curl -X POST http://127.0.0.1:7800/ -H 'X-Onto-Chiave: mario' --data-binary '[ "Mario"=nome, V=febbre ] = mario'
# SQL: via HTTP, o con i client di PostgreSQL (la parola d'ordine è il token)
cargo run -p onto-cli -- serve --port 7878 --pg-port 5433 --token segreto
curl -X POST http://127.0.0.1:7878/sql -H 'Authorization: Bearer segreto' --data-binary "SELECT nome FROM elementi WHERE febbre = V"
psql "host=127.0.0.1 port=5433 user=onto password=segreto dbname=onto"
# Aggiungere un gruppo: l'instradatore in funzione divide in due i dati del gruppo 0
cargo run -p onto-cli -- split --router host0:7800 --split 0=host5:7001
# I nodi ricordano la disposizione nuova: gli instradatori la adottano da soli
# Dati più grandi della RAM: la memoria del processo sta su un file mappato
cargo run -p onto-cli -- --memory-file memoria.bin --memory-file-gib 256 run examples/negozio.onto
# Benchmark comparativo (sweep Q1-Q8 su 4 motori, CSV in uscita)
cargo run -p onto-cli --release -- bench --sizes 1000,10000,100000,500000 --out risultati_bench.csv
# Micro-benchmark di regressione (criterion)
cargo bench
Esempio: il linguaggio come database
[ V=febbre, V=tosse ] = mario
[ V=febbre, F=tosse ] = anna
# La classe è intensionale: si calcola lazy sul motore popolato dagli
# elementi sopra — un elemento inserito dopo la definizione vi entra
# automaticamente se soddisfa il selettore.
class { V=febbre ^ V=tosse } = Febbricitanti
count{ Febbricitanti=class } = n : int # 1
# Membership: V o F, mai un default silenzioso.
if (mario in Febbricitanti) {
"mario è febbricitante" : print
}
if (anna in Febbricitanti) {
"anna è febbricitante" : print # non stampato: F
} else {
"anna NON è febbricitante" : print
}
# search_val/search_profile/top_k/distribution interrogano lo stesso motore.
search_profile{ V=febbre, V=tosse } = con_entrambi : list
"febbre e tosse (id nel motore): {con_entrambi}" : print # [0]
Struttura del workspace
onto-rs/
├── src/
│ ├── core/ tipo trivalente (TrivalentValue), MOVP (aggregato di proprietà) e peso epistemico (V=2,N=1,F=0)
│ ├── storage/ indice bitset trivalente (TrivalentIndex) + OntoEngine (motore unificato)
│ ├── graph/ grafo di relazioni tipate, integrato in OntoEngine
│ ├── lang/ lexer, parser, AST, interprete tree-walking
│ └── query/ LazyQuery: query trivalenti lazy sopra OntoEngine
├── onto-cli/ binario `onto`: run / repl / bench
├── examples/ script .onto dimostrativi (elementi, classi, motore, domini applicativi)
├── tests/ test d'integrazione (esegue tutti gli esempi)
├── benches/ micro-benchmark criterion
└── docs/ documentazione del linguaggio, architettura, benchmark, use case
Benchmark in sintesi
A N = 500 000 record (8 proprietà booleane trivalenti, 55% V / 30% F / 15% N),
ONTO è il motore più veloce su tutte le query testate contro SQLite
(rusqlite bundled), uno scan lineare senza indici e sled (KV embedded,
usato come proxy di Redis). Tempi in ms:
| Query | onto | sqlite | speedup sqlite/onto |
|---|---|---|---|
| Q1 scan singolo | 3.87 | 22.42 | 5.8× |
| Q2 AND 3 proprietà | 1.40 | 57.29 | 41× |
| Q3 AND 6 proprietà | 1.10 | 55.88 | 51× |
| Q5 distribuzione V/F/N | 0.017 | 15.82 | ~950× |
| Q7 top-20 per peso epistemico | 8.36 | 155.9 | 19× |
| Q8 scan a soglia | 3.76 | 408.6 | 109× |
Metodologia, tabelle complete per ogni N e note nella
docs/BENCHMARK.md.
Stato di produzione
- Copre l'intera superficie del linguaggio usata dagli script in
examples/, inclusomotore_db.onto(funzioni built-in di interrogazione del motore) eclassi.onto(classi dinamiche, fisse, aggregate, membership). - Test:
cargo test(unit + integrazione sugli esempi); qualità statica:cargo clippy. - Persistenza:
OntoEngine::save/load(snapshot binario bincode), esposti daonto run --save/--loade dalla REPL (:save/:load); per non perdere le scritture tra due snapshot c'è lo stato persistente con log di scrittura e replay automatico (storage::MotorePersistente), esposto daonto run --store <dir>eonto repl --store <dir>. - Concorrenza:
MotoreCondivisocondivide il motore tra thread, con letture in parallelo e scritture in esclusiva. - Linguaggio: transazioni (
transaction { … }), modifica e ritiro (set_property,retract), classi e ricerche su numeri e testi (valore, intervallo, testo libero per rilevanza con radici e sinonimi), campi unici, join anche con condizione, aggregazioni e raggruppamenti, SQL sulle classi (SELECTcon espressioni, funzioni, join, sottointerrogazioni,HAVING,UNION;INSERT,UPDATE,DELETE;information_schema), ricerca per frase, per parole simili e in 18 lingue, ricerca per vicinanza tra vettori, importazione di altri file. - Memoria: circa 6 byte per record con 8 proprietà; i record si
ricostruiscono dall'indice. Per dati più grandi della RAM,
--memory-filetiene tutta la memoria su un file mappato;MotorePartizionato(API Rust) tiene i record in partizioni su disco. - Rete:
onto serveespone il motore su HTTP o HTTPS, con token di accesso, letture in parallelo, stato su disco (istantanea più diario), replica e sostituzione automatica del primario a maggioranza;onto routeripartisce i dati tra più gruppi di nodi, con scritture «tutti o nessuno», interrogazioni SQL e join tra i gruppi, campi unici tra tutti;onto splitaggiunge un gruppo senza fermare il servizio. SQL anche con il protocollo di PostgreSQL (--pg-port): transazioni,WITH RECURSIVE, funzioni a finestra, liste, tabelle con tipi e chiavi esterne, viste, e i cataloghi con cui psql descrive lo schema. - Ricerca: per parole in 18 lingue (
search_text), per vicinanza tra vettori con indice dei vicini (nearest,vector_index), per parole che condividono i contesti (similar_text). - Limiti: in un join tra gruppi una sola tabella resta dov'è; di
PostgreSQL ci sono SQL e lo schema, non procedure ed estensioni; oltre
la RAM ogni lettura sparsa costa un accesso al disco — onerosità e strategie di deployment in
docs/USE_CASES.md§ Limiti.