La libreria Lean di lungo è il pacchetto Lake lungo, rilasciato con lungo alla versione di lungo
(i suoi sorgenti sono lean/ in
lungo-rs). Un progetto la importa per dichiarare ciò che lungo porta nei pacchetti che genera
(garanzia formale): gli attributi scrivono record che il
worker legge, e i moduli di supporto contengono le definizioni con cui di solito si enunciano i
claim.
Richiedere la libreria
Da un checkout o da una copia inclusa nel progetto, per percorso:
# lakefile.toml
[[require]]
name = "lungo"
path = "../lungo-rs/lean"
Dal repository lungo-rs, al tag del lungo con cui si genera:
[[require]]
name = "lungo"
git = "https://github.com/machinefabric/lungo-rs"
rev = "v1.88.0"
subDir = "lean"
Si esegua lake update una volta e si faccia il commit di lake-manifest.json: le build di lungo
non scaricano mai dipendenze. La libreria fissa la stessa toolchain di Lean di lungo
(leanprover/lean4:v4.34.1).
Poi import Lungo. Importa due parti:
Lungo.Core:Lungo.Registrye i moduli di supporto qui sotto. Importano soloInite non dichiarano inizializzatori, quindi un programma contiene esattamente il loro codice che chiama.Lungo.Attr: gli attributi. Hanno bisogno dell’elaboratore di Lean e girano solo mentre il progetto viene compilato; lungo non li collega mai a un programma, né collega le parti di Lean che importano.
I record scritti dagli attributi sono versionati da Lungo.Registry.schemaVersion (1). lungo
rifiuta una libreria la cui versione non sa leggere (LNG0709).
Tipi
Un tipo di specifica, una relazione di claim, un ruolo e un identificatore di facility sono
stringhe con spazio dei nomi: due o più segmenti separati da . fatti di lettere minuscole,
cifre e _ (e - dopo il primo segmento), ciascuno che inizia con una lettera: lungo.decides,
acme.cost-bound, timing.clock.
Lo spazio dei nomi lungo è proprio di lungo e contiene solo questi:
| Propri di lungo | |
|---|---|
| Tipi di specifica | lungo.relation, lungo.contract, lungo.model, lungo.state, lungo.protocol, lungo.property |
| Relazioni di claim | lungo.decides, lungo.satisfies, lungo.refines, lungo.preserves, lungo.equals, lungo.roundtrip, lungo.law, lungo.monitors |
| Ruoli | lungo.implementation, lungo.oracle, lungo.monitor, lungo.model |
| Identificatori di facility | nessuno: l’identificatore di una facility sta in uno spazio dei nomi proprio |
Ogni altro spazio dei nomi appartiene a chi lo definisce: lungo ne controlla la grafia, lo porta in
ogni pacchetto e non gli attribuisce alcun significato. Un pacchetto può definire così relazioni e
tipi propri (examples/semantic-model definisce acme.cost-bound) senza alcuna modifica a lungo.
Attributi
Ogni attributo è globale: gli usi local e scoped vengono rifiutati, e così pure darne uno a una
dichiarazione di un altro modulo (un record si legge dove sta la sua dichiarazione). Una
dichiarazione riceve ciascun attributo una sola volta. Ognuno scrive un record, la costante interna
decl._lungo_<tipo>; il worker controlla di nuovo ogni record quando legge il progetto compilato
(LNG0701–LNG0707).
@[lungo_spec "kind"]
Marca una specifica del tipo dato. Qualunque dichiarazione può esserlo. Una specifica che è un
oggetto logico (un contratto, una relazione, un protocollo) è meglio noncomputable, così non viene
esportata.
@[lungo_spec "lungo.contract"]
noncomputable def SortsAscending : Contract (List Nat) (List Nat) := sortedPermutation fun a b => decide (a ≤ b)
@[lungo_claim "relation" subject f … spec S …]
Sul teorema che dimostra il claim: i suoi soggetti f … stanno nella relazione relation con
le sue specifiche S …. subject è seguito da una o più dichiarazioni; spec e le specifiche
sono facoltativi.
@[lungo_claim "lungo.satisfies" subject sort spec SortsAscending]
theorem sort_satisfies : Satisfies sort SortsAscending := …
Controlli: la dichiarazione è un teorema; ogni soggetto esiste, non è un teorema ed è menzionato
dall’enunciato; ogni specifica ha @[lungo_spec] ed è menzionata dall’enunciato; nessuno è nominato
due volte; e per una relazione lungo.*, l’enunciato ha la forma che la relazione
richiede. Si legge solo l’enunciato, mai la dimostrazione. Le assunzioni a cui un claim è
condizionato non si dichiarano: lungo le trova tra i tipi dei binder ∀ iniziali dell’enunciato.
@[lungo_facility "ns.name"]
Nomina una facility che l’host fornisce, con il suo identificatore. Si dà a qualunque dichiarazione
che non sia un teorema (di solito una structure vuota), oppure a un’istanza di
Lungo.Async.Interface Op, che crea una facility asincrona le cui operazioni sono i costruttori
di Op.
@[lungo_facility "timing.clock"]
structure Clock
inductive StoreOp where
| get (key : String)
| put (key value : String)
@[lungo_facility "store.kv"]
instance storeOps : Lungo.Async.Interface StoreOp where
Ret
| .get _ => Option String
| .put _ _ => Unit
Per una facility asincrona, Op deve essere un tipo induttivo senza parametri né indici, nessun
campo di un costruttore può essere una prova o un tipo, e ciò che ogni costruttore riceve in
risposta (Ret) deve ridursi a un tipo che non dipende dai campi del costruttore
(LNG0707).
@[lungo_operation C]
Su una dichiarazione @[extern]: è un’operazione della facility C, che l’host implementa. Si
scriva prima @[extern "symbol"]; l’attributo si applica dopo che la dichiarazione è stata
compilata.
@[extern "timing_now", lungo_operation Clock]
opaque now (u : Unit) : IO Nat
Controlli: C ha @[lungo_facility] e non è una facility asincrona; la dichiarazione ha
@[extern]. lungo richiede inoltre che un’operazione abbia una voce per C e non sia implementata
dal runtime o da un @[export] (LNG0706). Un’operazione il cui tipo Lean non
è IO o EIO è una funzione per Lean, che può valutarne una chiamata una sola volta e conservarne
il valore: l’implementazione dell’host deve rispondere lo stesso agli stessi argomenti.
@[lungo_assumption C]
Su una proposizione (o una famiglia di proposizioni, o una classe in Prop): viene assunta, mai
dimostrata, dell’implementazione della facility C da parte dell’host. Un claim che la prende come
ipotesi è condizionato a essa.
@[lungo_assumption Clock]
def Ticks : Prop := 0 < ticksPerSecond ()
@[lungo_role "role"]
Su una definizione che un programma esegue (non un teorema): a che cosa serve. lungo.oracle
marca un’implementazione di riferimento con cui si verifica altro codice; lungo.monitor,
lungo.model e lungo.implementation sono gli altri ruoli di lungo.
Relazioni
Un claim in una delle relazioni di lungo deve enunciarla nella sua forma, così che la relazione
significhi la stessa cosa in ogni pacchetto. La forma si legge dall’enunciato dopo i suoi ∀
iniziali; f menziona ogni soggetto significa che ogni soggetto compare in f.
| Relazione | L’enunciato, dopo i suoi ∀ |
|---|---|
lungo.decides |
f … = true ↔ P …, con il lato sinistro che menziona ogni soggetto |
lungo.equals |
lhs = rhs, con lhs che menziona ogni soggetto |
lungo.roundtrip |
lhs = rhs, con lhs che menziona ogni soggetto e rhs nessuno, come decode (encode x) = some x |
lungo.satisfies |
Lungo.Spec.Satisfies f c o Lungo.Spec.SatisfiesExcept f c, con f che menziona ogni soggetto |
lungo.refines |
Lungo.Spec.Refines impl spec, con impl che menziona ogni soggetto |
lungo.preserves |
Lungo.Spec.StateSpec.Preserves s exec o Lungo.Spec.StateSpec.Implements s exec, con exec che menziona ogni soggetto |
lungo.monitors |
Lungo.Monitor.Sound m p, con m che menziona ogni soggetto |
lungo.law |
qualunque enunciato che menzioni ogni soggetto |
Moduli di supporto
Lungo.Spec.Contract
Contract α βconpre : α → Propepost : α → β → Prop;Satisfies f c: ogni input che soddisfapredà un output che soddisfapost.ExceptContract α ε βaggiungerefuses : α → ε → Prop;SatisfiesExcept f cper una funzione che restituisceExcept ε β: restituisce un output che soddisfapost, oppure rifiuta comerefusesconsente.Contract.Strengthens strong weakesatisfies_weaken;Contract.seqesatisfies_compper due funzioni in sequenza.
Lungo.Spec.Model
BehavioralModel βconadmits : β → Prop;Refines impl spec: ogni comportamento cheimplammette,speclo ammette.BehavioralModel.ofFunction f,ofRelation r,function_refines_relation,intererefines_inter;refines_reflerefines_trans.
Lungo.Spec.State
StateSpec σ ι οconinit,invariantestep : σ → ι → ο → σ → Prop.- Per un passo eseguibile
exec : σ → ι → ο × σ:StateSpec.Preserves s exec(gli stati iniziali soddisfano l’invariante e ogni passo lo mantiene) eStateSpec.Implements s exec(dagli stati che soddisfano l’invariante, ogni passo è uno chestepammette). StateSpec.run exec,preserves_runereachable_invariant: un invariante preservato vale lungo ogni esecuzione da uno stato iniziale.
Lungo.Trace
Protocol ε: le tracce che ammette (allowed), chiuse per prefissi;Protocol.any,always p,ofSteps start next,both,Withinewithin_trans.Reducer ε σconstartestep : σ → ε → Option σ;Reducer.run,Reducer.protocoleaccepts_iff.ReducerRefines r p(ogni traccia cheraccetta,pla ammette) erefines_of_invariant, che lo dimostra da un invariante che lega gli stati alle tracce che li raggiungono.Hyperproperty εeNoninterference r low observe, per proprietà su più tracce.
Lungo.Monitor
Monitor ε σconstartestep : σ → ε → Except String σ: l’host passa gli eventi uno alla volta; un errore è una violazione e il suo motivo.Monitor.run,Accepts, eSound m p:maccetta esattamente le tracce chepammette.- Monitor con la loro correttezza:
ofPredicate(ofPredicate_sound),ofReducer(ofReducer_sound) eboth(both_sound).
Lungo.Async
Interface OpconRet : Op → Type, ciò che ogni operazione riceve in risposta.Program Op α:done valueoppurecall op resume; unaMonad, conProgram.perform op. Un export che restituisce unProgramè una funzione asincrona in ogni linguaggio, che riceve un handler che esegue le operazioni.Program.run handler: interpreta un programma in qualunque monade, così i claim possono eseguirlo contro un modello dell’host.Exchange,Admits p exchanges a,trace answer pe i teoremiadmits_trace,run_id,admits_uniqueeadmits_bind.
I test della libreria
lake build LungoTest controlla, con #guard_msgs, ogni rifiuto di ogni attributo.