In questa sezione

Questa guida mostra come il codice Lean chiama una funzione dell’applicazione, nel linguaggio del pacchetto generato. Per Rust, vedi come chiamare funzioni Rust da Lean; per operazioni a cui l’applicazione risponde in modo asincrono, vedi come chiamare operazioni asincrone dell’host.

Dichiara una facility in Lean

Ciò che l’host fornisce è una facility: una dichiarazione registrata con @[lungo_facility], le cui operazioni sono dichiarazioni @[extern] marcate @[lungo_operation]. Il progetto richiede la libreria Lean di lungo per gli attributi.

import Lungo

namespace Store

/-- The application's store. -/
@[lungo_facility "store.data"]
structure Data

/-- The application's log. -/
@[lungo_facility "store.journal"]
structure Journal

/-- Looks a key up in the application's store. -/
@[extern "store_lookup", lungo_operation Data]
opaque lookup (key : @& String) : Option Nat

/-- Appends a line to the application's log. -/
@[extern "store_log", lungo_operation Journal]
opaque log (line : String) : IO Unit

def total (keys : List String) : IO Nat := do
  let mut sum := 0
  for k in keys do
    match lookup k with
    | some v => log s!"{k} = {v}"; sum := sum + v
    | none => throw (IO.userError s!"unknown key {k}")
  return sum

end Store

L’identificatore della facility (store.data) la nomina in ogni pacchetto, e il suo ultimo segmento la nomina in ciascun linguaggio (Data, set_data, data). Un extern che non è operazione di alcuna facility e che né il runtime né il programma implementano è un errore (LNG0401); lo è anche un’operazione che il runtime o un @[export] Lean implementano già (LNG0706).

Ciò che Lean non può controllare dell’host, può assumerlo: una proposizione @[lungo_assumption Data] dice che cosa deve soddisfare l’implementazione dell’host, e i claim che vi si appoggiano lo dichiarano in ogni pacchetto (vedi come dichiarare facility e assunzioni).

Implementala

Il pacchetto generato ha, per ogni facility, un’interfaccia (o un protocollo) con un metodo per operazione, con il nome della dichiarazione Lean e i suoi tipi, e una funzione che la installa. Un’operazione IO fallisce con l’errore di IO del linguaggio (qualsiasi altro errore diventa IO.userError con il suo messaggio); un’operazione che non è IO né EIO non deve fallire: un suo fallimento termina il programma. Un’operazione il cui tipo Lean è puro deve anche comportarsi da funzione pura, rispondendo allo stesso modo agli stessi argomenti: Lean può calcolare lookup "a" una volta sola e conservarne il valore.

Installa ogni facility prima della prima chiamata del programma. Una chiamata precedente fallisce indicando la facility e una sua operazione: Go restituisce un *lungo.MissingFacilityError, Python solleva lungo_py.MissingFacilityError, Swift lancia LungoMissingFacility, il load di TypeScript rifiuta con MissingFacilityError e un programma C termina con un messaggio.

Go

type data struct{ values map[string]*big.Int }

func (d *data) Lookup(key string) (lungo.Option[*big.Int], error) {
	v, ok := d.values[key]
	return lungo.Option[*big.Int]{Value: v, Valid: ok}, nil
}

type journal struct{}

func (journal) Log(line string) error {
	log.Println(line)
	return nil
}

store.SetData(&data{values: map[string]*big.Int{"a": big.NewInt(1)}})
store.SetJournal(journal{})

Python

class Data:
    def lookup(self, key: str):
        return {"a": 1}.get(key)

class Journal:
    def log(self, line: str) -> None:
        print(line)

store.set_data(Data())
store.set_journal(Journal())

Swift

final class Table: StoreData {
    func lookup(_ key: String) throws -> LungoNat? { key == "a" ? 1 : nil }
}

final class Console: StoreJournal {
    func log(_ line: String) throws { print(line) }
}

setData(Table())
setJournal(Console())

TypeScript

const p = await load({
  facilities: {
    data: { lookup: (key) => (key === "a" ? 1n : null) },
    journal: { log: (line) => console.log(line) },
  },
});

C

static int32_t lookup(void *ctx, const lungo_value *const *args, size_t n, lungo_value **result, lungo_error **error) {
    size_t len;
    const char *key = lungo_value_get_string(args[0], &len);
    *result = len == 1 && key[0] == 'a' ? lungo_value_some(lungo_value_nat(1)) : lungo_value_none();
    return LUNGO_OK;
}

static int32_t log_line(void *ctx, const lungo_value *const *args, size_t n, lungo_value **result, lungo_error **error) {
    size_t len;
    const char *line = lungo_value_get_string(args[0], &len);
    printf("%.*s\n", (int)len, line);
    *result = lungo_value_unit();
    return LUNGO_OK;
}

store_implement_data_lookup(lookup, NULL, NULL);
store_implement_journal_log(log_line, NULL, NULL);

Vedi di che cosa ha bisogno il programma

lungo assurance elenca, per ogni export, le facility di cui ha bisogno e le assunzioni che i suoi claim fanno su di esse; ogni pacchetto porta le stesse informazioni nel suo documento di garanzia (vedi il documento di garanzia).

Passa funzioni in entrambe le direzioni

Un parametro Lean di tipo funzione accetta una funzione del linguaggio dell’host, che Lean può chiamare un numero qualsiasi di volte, su qualsiasi thread, finché la trattiene; una funzione Lean restituita all’host è una funzione che l’host chiama. Entrambe attraversano il confine come descritto nel formato di trasmissione; non serve dichiarare nulla.