Questa guida mostra come permettere al codice Lean di chiamare una funzione della tua applicazione Rust: dichiararla in Lean come operazione di una facility, implementarla in Rust e mappare l’una sull’altra.
Dichiara la funzione in Lean
Una facility è ciò che l’applicazione fornisce, registrata con @[lungo_facility]; le sue
operazioni sono costanti opache con il simbolo che il lato Rust implementerà, marcate
@[lungo_operation]. Gli attributi vengono dalla libreria Lean di lungo,
che il progetto Lake richiede:
import Lungo
namespace Host
/-- The services the application provides. -/
@[lungo_facility "app.services"]
structure Services
/-- Looks a key up in the host's table. -/
@[extern "host_lookup", lungo_operation Services]
opaque hostLookup (key : @& String) : Option Nat
/-- Appends a line to the host's log. -/
@[extern "host_log", lungo_operation Services]
opaque hostLog (line : String) : IO Unit
end Host
Il tipo del risultato ha bisogno di un’istanza Inhabited (Lean ne richiede una per
opaque). @& indica un parametro che Lean presta anziché trasferire; la funzione Rust riceve
in ogni caso un valore Rust posseduto. Ciò che le dimostrazioni del programma richiedono
all’implementazione, e non possono controllare, si dichiara come assunzione della facility
(vedi come dichiarare facility e assunzioni).
Usala da Lean come qualsiasi altra funzione:
def resolve (k : String) : IO Nat := do
match Host.hostLookup k with
| some v => Host.hostLog s!"{k} => {v}"; return v
| none => throw (IO.userError s!"unknown key: {k}")
Implementala in Rust
Scrivi una funzione i cui parametri e il cui risultato sono le forme Rust dei tipi Lean (vedi
I tipi Lean in Rust). IO α diventa
Result<A, lungo::IoError>:
pub mod host {
use lungo::{IoError, Nat};
pub fn lookup(key: String) -> Option<Nat> {
match key.as_str() {
"alpha" => Some(Nat::from(1u64)),
_ => None,
}
}
pub fn log(line: String) -> Result<(), IoError> {
if line.is_empty() {
return Err(IoError::user("empty log line"));
}
eprintln!("{line}");
Ok(())
}
}
Un Err restituito da una funzione IO viene sollevato in Lean come quell’IO.Error. La
funzione di un’operazione il cui tipo Lean è puro deve comportarsi da funzione pura,
rispondendo allo stesso modo agli stessi argomenti: Lean può calcolare una tale chiamata una
volta sola e conservarne il valore.
Mappa i simboli
In build.rs, mappa ogni simbolo sul percorso Rust della sua implementazione, visto dalla
radice del crate:
fn main() -> lungo_build::Result<()> {
lungo_build::configure()
.rust_extern("host_lookup", "crate::host::lookup")
.rust_extern("host_log", "crate::host::log")
.compile_lean("lean")
}
(con il comando lungo, le stesse mappature sono la tabella [rust.rust-externs] di
lungo.toml).
Compila. Se un’operazione non ha un’implementazione, la build fallisce con
LNG0401, indicando la dichiarazione e la firma Rust di cui
ha bisogno; così anche un extern che non è operazione di alcuna facility, con il suggerimento
di marcarlo @[lungo_operation]. Mappare un simbolo che non è di un’operazione fallisce con
LNG0706, e una mappatura per un simbolo che nessuno usa con
LNG0405.
Passa funzioni attraverso il confine
Un parametro funzione di Lean arriva in Rust come LeanClosure, che Rust può chiamare:
@[extern "host_transform", lungo_operation Services]
opaque hostTransform (f : Nat → Nat) (xs : List Nat) : List Nat
use lungo::{LeanClosure, List, Nat};
pub fn transform(f: LeanClosure<fn(Nat) -> Nat>, xs: List<Nat>) -> List<Nat> {
xs.into_iter().map(|x| f.call(x)).collect()
}
Nella direzione opposta, Rust passa le proprie funzioni alle funzioni Lean generate con
LeanClosure::from_fn(|n: Nat| …).
In modalità LeanOracle
Le stesse mappature funzionano in modalità LeanOracle per gli extern dichiarati
@[extern "symbol"]: l’adattatore generato viene esportato con quel simbolo C. Le altre forme
di extern falliscono con LNG0407.
Le operazioni a cui l’applicazione risponde in modo asincrono sono invece una facility
asincrona, i cui export diventano async fn che ricevono un handler (vedi
come chiamare operazioni asincrone dell’host).
Un esempio completo è
compiler-tests/host
in lungo-rs.