Repository navigation
Sorgenti esempi
Sorgenti completi di examples/ (Triad-Full, non ancora eseguibili: i loro output sono nel Catalogo conformance Full) e dei task del benchmark AI in ai/tasks/ (Triad-Core, eseguibili).
examples/assaggio.tdexamples/benvenuto.triadexamples/famiglia.tdexamples/garanzie.tdexamples/hello_shebang.tdexamples/macro.tdexamples/tour.tdai/tasks/01_sommaai/tasks/02_fattorialeai/tasks/03_fibonacciai/tasks/04_stringheai/tasks/05_chiusure
# assaggio.td — i tre linguaggi in poche righe
struct Point
x::Float
y::Float
end
function dist(a::Point, b::Point)::Float
requires a.x != b.x || a.y != b.y
sqrt((a.x - b.x)^2 + (a.y - b.y)^2)
end
fact parent(:alice, :bob)
fact parent(:bob, :dave)
rule grandparent(?x, ?z) :- parent(?x, ?y), parent(?y, ?z)
macro unless(c, body)
:(if !$c; $body; end)
end
result = solve grandparent(:alice, ?who) limit 5
# `solve` restituisce un valore normale: una lista di soluzioni
println("nipoti di alice: ", result)
println("primo: ", result[1].who)
println("quanti: ", length(result))
p = Point(0, 0)
q = Point(3, 4)
println("dist(p, q) = ", dist(p, q))
# il contratto violato è un errore come dato
e = try dist(p, p) end
println("dist(p, p) -> ", e)
println("tipo: ", e.kind)
@unless(length(result) == 0, println("almeno un nipote"))
# un predicato chiamato come funzione: vero se dimostrabile
println("alice è nonna di dave? ", grandparent(:alice, :dave))#!/usr/bin/env triad
# benvenuto.triad — file col nome canonico dell'estensione (.triad).
# Esegui con: triad examples/benvenuto.triad (oppure ./benvenuto.triad)
println("Triad, standard ", 0.2)
fact saluta(:mondo)
println((solve saluta(?x))[1].x)# famiglia.td — Prolog dentro Triad: fatti, regole, ricorsione, liste
fact parent(:tom, :bob)
fact parent(:tom, :liz)
fact parent(:bob, :ann)
fact parent(:bob, :pat)
fact parent(:pat, :jim)
fact female(:liz)
fact female(:ann)
fact female(:pat)
rule mother(?m, ?c) :- parent(?m, ?c), female(?m)
rule sibling(?a, ?b) :- parent(?p, ?a), parent(?p, ?b), ?a != ?b
rule ancestor(?a, ?d) :- parent(?a, ?d)
rule ancestor(?a, ?d) :- parent(?a, ?x), ancestor(?x, ?d)
# solve restituisce una lista: si filtra e si trasforma con il resto del linguaggio
discendenti = [s.d for s in solve ancestor(:tom, ?d)]
println("discendenti di tom: ", discendenti)
println("fratelli di ann: ", [s.b for s in solve sibling(:ann, ?b)])
println("madri: ", [string(s.m, " -> ", s.c) for s in solve mother(?m, ?c)])
# negazione per fallimento: chi non ha figli?
persone = [:tom, :bob, :liz, :ann, :pat, :jim]
foglie = filter(p -> !parent(p, ?_), persone)
println("senza figli: ", foglie)
# aritmetica nei goal con `is`, e una funzione Triad usata come goal
fact born(:tom, 1950)
fact born(:bob, 1975)
fact born(:pat, 2001)
rule age(?p, ?a) :- born(?p, ?y), ?a is 2026 - ?y
over50(a::Int) = a > 50
println("over 50: ", [s.p for s in solve age(?p, ?a) && over50(?a)])
# liste in stile Prolog: [testa | coda]
rule app([], ?l, ?l)
rule app([?h | ?t], ?l, [?h | ?r]) :- app(?t, ?l, ?r)
println("modi di spezzare [1, 2, 3]:")
for s in solve app(?a, ?b, [1, 2, 3])
println(" ", s.a, " ++ ", s.b)
end
# un identificatore nudo in un termine è un valore dell'host
chi = :bob
println("figli di ", chi, ": ", [s.c for s in solve parent(chi, ?c)])# garanzie.td — errori come dati, contratti, budget, tracce, effetti
# --- contratti: requires / ensures fanno parte della funzione ---
function safe_div(a::Number, b::Number)::Float
requires b != 0
ensures abs(result * b - a) < 1e-9
a / b
end
println(safe_div(10, 4))
e = try safe_div(1, 0) end # try senza catch: l'errore diventa il valore
println("violazione: ", e.kind, " — ", e.msg)
println("clausola: ", e.data.clause, ", funzione: ", e.data.fn)
# --- errori come dati: si ispezionano, si passano, si fa dispatch ---
describe(e::Err) = string("errore ", e.kind, ": ", e.msg)
describe(x::Any) = string("valore ", x)
risultati = [try div(10, d) end for d in [2, 0, 5]]
for r in risultati
println(describe(r))
end
function parse_eta(s::String)
n = try Int(s) end
if iserr(n)
return err(:input, "età non valida", s)
end
requires n >= 0
n
end
println(describe(parse_eta("42")))
println(describe(parse_eta("quaranta")))
try
throw(err(:mio, "qualcosa non va", [1, 2, 3]))
catch e
println("catturato ", e.kind, " con dati ", e.data)
end
# --- budget: un calcolo limitato finisce con un Err, non si blocca ---
r = with budget(steps = 10_000)
i = 0
while true
i += 1
end
end
println("ciclo infinito -> ", r.kind)
rule loop(?x) :- loop(?x)
println("solve infinito -> ", (try solve loop(1) steps 2000 end).kind)
# --- tracce: la storia di un calcolo è un valore ---
fib(n::Int) = n < 2 ? n : fib(n - 1) + fib(n - 2)
t = with trace
fib(3)
end
println("fib(3) = ", t.result, " in ", length(t.trace), " eventi")
println("chiamate: ", [e.args for e in t.trace if e.port == :call])
fact edge(:a, :b)
fact edge(:b, :c)
rule path(?x, ?y) :- edge(?x, ?y)
rule path(?x, ?y) :- edge(?x, ?z), path(?z, ?y)
t = solve path(:a, :c) trace
for e in t.trace
println(" ", e.port, " ", e.goal, " @", e.depth)
end
# --- effetti: pure per default, !io / !state dichiarati ---
function saluta(nome::String) !io
println("ciao, ", nome)
end
saluta("Triad")
function quadrato(x::Number)
x * x # pura: niente println qui
end
function rumorosa(x::Number)
println(x) # vietato: non dichiara !io
end
println("effetto non dichiarato -> ", (try rumorosa(1) end).msg)
function impara(x) !state
fact visto($x)
end
impara(:gatto)
println("imparato: ", [s.v for s in solve visto(?v)])#!/usr/bin/env triad
# =====================================================================
# hello_shebang.td — esempio di file .td eseguibile direttamente
#
# Rendi eseguibile e lancia:
# chmod +x hello_shebang.td
# ./hello_shebang.td Alice Bob
#
# Oppure con il launcher:
# triad hello_shebang.td Alice Bob
#
# La prima riga (shebang) viene rimossa automaticamente dal runtime.
# =====================================================================
function saluta(nome::String) !io
println("Ciao, ", nome, "! Benvenuto in Triad.")
end
# Mostra gli argomenti passati dalla riga di comando
println("=== File .td eseguibile ===")
println("ARGS = ", ARGS)
if isempty(ARGS)
println("Uso: ./hello_shebang.td nome1 [nome2 ...]")
println("Provo con un esempio:")
saluta("mondo")
else
for nome in ARGS
saluta(String(nome))
end
end
# Dimostra che la logica funziona anche nei file shebang
fact parent(:alice, :bob)
fact parent(:bob, :charlie)
rule grandparent(?x, ?z) :- parent(?x, ?y), parent(?y, ?z)
println()
println("Query logica: grandparent(:alice, ?who)")
result = solve grandparent(:alice, ?who)
println("Risultato: ", result)# macro.td — il codice è dato (Lisp), con la faccia di Julia
# un pezzo di codice è un valore: un albero con head e args
c = :(a.x + f(b, 2))
println("codice: ", c)
println("head: ", c.head)
println("args: ", c.args)
println("sexpr: ", sexpr(c))
# $ inserisce valori nel codice; eval lo esegue
n = 7
quadrato = :($n * $n)
println(quadrato, " = ", eval(quadrato))
# parse/unparse: testo <-> albero
t = parse("if x > 0; :pos; else; :neg; end")
println("parse: ", sexpr(t))
println("unparse: ", unparse(t))
# macro: ricevono codice, restituiscono codice
macro unless(c, body)
:(if !$c; $body; end)
end
macro swap(a, b)
tmp = gensym(:tmp) # nome fresco: niente catture accidentali
quote
$tmp = $a
$a = $b
$b = $tmp
end
end
macro time_it(label, e)
:(begin
r = $e
println($label, " -> ", r)
r
end)
end
x = 1
y = 2
@swap(x, y)
println("dopo @swap: x = ", x, ", y = ", y)
@unless(x < y, println("x non è minore di y"))
@time_it("somma", reduce(+, 1:100))
# costruire codice con un programma: un polinomio come albero
function poly(coeffs)
acc = :(0)
i = 0
for c in coeffs
acc = :($acc + $c * x ^ $i)
i += 1
end
acc
end
p = poly([1, 2, 3])
println("polinomio: ", p)
x = 2
println("p(2) = ", eval(p))# tour.td — un giro guidato di Triad 2.0.
# Scritto leggendo TUTORIAL.md: tocca i tre paradigmi (Julia ∪ Prolog ∪ Lisp)
# e le caratteristiche del linguaggio (errori come dati, contratti, budget,
# tracce, effetti, destrutturazione).
#
# Esegui: julia --startup-file=no triad.jl examples/tour.td
# =====================================================================
# 1. Julia: struct, tipi, operatori-funzione, multiple dispatch
# =====================================================================
struct Punto
x::Float
y::Float
end
function +(a::Punto, b::Punto)
Punto(a.x + b.x, a.y + b.y)
end
function norma(p::Punto)::Float
sqrt(p.x^2 + p.y^2)
end
p = Punto(3, 4) # Int → Float nella costruzione
q = Punto(1, 2)
r = p + q
println("|p| = ", norma(p))
println("p + q = (", r.x, ", ", r.y, ")")
area(n::Int) = :intero
area(n::Number) = :numero
println("dispatch: area(1) = ", area(1), ", area(1.5) = ", area(1.5))
# =====================================================================
# 2. Contratti: requires / ensures sono parte della funzione
# =====================================================================
function dividi(a::Number, b::Number)::Float
requires b != 0
ensures abs(result * b - a) < 1e-9
a / b
end
println("dividi(7, 2) = ", dividi(7, 2))
e = try dividi(1, 0) end
println("contratto violato -> ", e.kind, " (clausola ", e.data.clause, ")")
# =====================================================================
# 3. Errori come dati: try ... end, iserr, dispatch su Err
# =====================================================================
describe(e::Err) = string("errore ", e.kind)
describe(x::Any) = string("valore ", x)
for r in [try div(10, d) end for d in [5, 0]]
println(" ", describe(r))
end
e = try (a, b) = [1] end
println("destrutturazione con arità sbagliata -> ", e.kind)
# =====================================================================
# 4. Prolog: fatti, regole, ricorsione, `is`, negazione per fallimento
# =====================================================================
fact volo(:roma, :milano, 60)
fact volo(:milano, :parigi, 90)
fact volo(:parigi, :londra, 80)
fact volo(:roma, :parigi, 180)
rule rotta(?a, ?b, ?c) :- volo(?a, ?b, ?c)
rule rotta(?a, ?b, ?c) :- volo(?a, ?x, ?c1), rotta(?x, ?b, ?c2), ?c is ?c1 + ?c2
println("rotte da roma (limit 6):")
for s in solve rotta(:roma, ?b, ?c) limit 6
println(" ", s.b, " a ", s.c, " minuti")
end
println("roma -> londra costa ", (solve rotta(:roma, :londra, ?c))[1].c, " minuti")
citta = [:roma, :milano, :parigi, :londra]
terminali = filter(c -> !volo(c, ?_, ?_), citta)
println("senza voli in partenza: ", terminali)
# =====================================================================
# 5. Destrutturazione: assegnamento multiplo e for su coppie
# =====================================================================
(x, y) = [10, 32]
println("x + y = ", x + y)
totale = 0
for (c, v) in [[:alfa, 1], [:beta, 2], [:gamma, 3]]
println(" ", c, " -> ", v)
totale += v
end
println("totale = ", totale)
# =====================================================================
# 6. Lisp: macro e codice come dato
# =====================================================================
macro unless(c, body)
:(if !$c; $body; end)
end
@unless(2 > 3, println("macro: 2 non è maggiore di 3"))
function poly(coeffs)
acc = :(0)
i = 0
for c in coeffs
acc = :($acc + $c * x ^ $i)
i += 1
end
acc
end
espressione = poly([1, 2, 3])
x = 2
println("polinomio p(2) = ", eval(espressione))
# =====================================================================
# 7. Budget e tracce: sono valori, non blocchi
# =====================================================================
r = with budget(steps = 2000)
i = 0
while true
i += 1
end
end
println("budget esaurito -> ", r.kind)
fib(n::Int) = n < 2 ? n : fib(n - 1) + fib(n - 2)
t = with trace
fib(5)
end
println("fib(5) = ", t.result, " (", length(t.trace), " eventi tracciati)")
# =====================================================================
# 8. Effetti dichiarati: pure per default, !io / !state espliciti
# =====================================================================
function saluta(nome::String) !io
println("ciao, ", nome, "!")
end
saluta("Triad")
function muta(x::Number)
println(x)
end
println("effetto non dichiarato -> ", (try muta(1) end).kind)
function impara(x) !state
fact visto($x)
end
impara(:gatto)
println("imparato: ", [s.v for s in solve visto(?v)])Verifica: node cli/triad-cli.mjs ai verify (vedi Parte IV §40).
Richiesta (task.md):
Scrivi un programma Triad che stampi il risultato di:
2 + 3 * 4Output atteso: una sola riga con il numero, poi newline. Non stampare altro.
Soluzione di riferimento (solution.triad):
println(2 + 3 * 4)Output atteso:
14
Richiesta (task.md):
Scrivi un programma Triad che definisca una funzione ricorsiva
fattoriale(n)e stampifattoriale(5).Output atteso: una sola riga con il risultato, poi newline.
Soluzione di riferimento (solution.triad):
fattoriale(n) = n < 2 ? 1 : n * fattoriale(n - 1)
println(fattoriale(5))Output atteso:
120
Richiesta (task.md):
Scrivi un programma Triad che definisca una funzione ricorsiva
fib(n)e stampifib(10).Output atteso: una sola riga con il risultato, poi newline.
Soluzione di riferimento (solution.triad):
fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
println(fib(10))Output atteso:
55
Richiesta (task.md):
Scrivi un programma Triad che:
- assegni a
nomela stringa"Triad";- stampi la concatenazione di
"ciao "enome;- stampi la lunghezza di
nome.Output atteso: due righe, la prima
ciao Triad, la seconda5.
Soluzione di riferimento (solution.triad):
nome = "Triad"
println("ciao " * nome)
println(length(nome))Output atteso:
ciao Triad
5
Richiesta (task.md):
Scrivi un programma Triad che:
- definisca una funzione
crea(n)che restituisce una lambdax -> x + n;- chiami
crea(10)e assegni il risultato af;- stampi
f(5).Output atteso: una sola riga con il risultato, poi newline.
Soluzione di riferimento (solution.triad):
crea(n) = x -> x + n
f = crea(10)
println(f(5))Output atteso:
15
Parte prima
Parte seconda 4. Le parole del linguaggio 5. Valori e tipi 6. Espressioni, operatori e variabili 7. Decidere e ripetere 8. Funzioni e ricorsione 9. Lambda, chiusure e visibilità dei nomi 10. Liste e dati strutturati 11. Stringhe, simboli e byte 12. Quando le cose vanno storte 13. Progetto: un piccolo interprete
Parte terza 14. Tipi, struct e multiple dispatch 15. Programmazione logica 16. Codice come dato e macro 17. Garanzie, costrutti Full e interfaccia
Parte quarta 18. Anatomia del progetto e contratto di conformità 19. L'interprete di riferimento 20. Il seed in codice macchina 21. Verso il self-hosting 22. Gli strumenti di sviluppo
Appendici
- Riferimento dei builtin Core
- Catalogo conformance Core
- Catalogo conformance Full
- Sorgenti degli esempi
- Sorgenti self-hosted
- Documenti di riferimento
- Indice analitico
Manuale di consultazione