In this section

lungo.h is the interface of the prebuilt runtime library (liblungo). It is included in every runtime package (include/lungo.h) and every generated package. Its C ABI has a version, LUNGO_ABI_VERSION, currently 2; generated code calls the function lungo_abi_v<N> (lungo_abi_v2), which only a runtime of that ABI defines, so it cannot link with a runtime of another ABI.

The header has three parts: the value API, for C and Objective-C programs using a generated C package; the program API and the boundary, for generated code and the support libraries; and the runtime’s primitives (lungo_<symbol> for every Lean @[extern] symbol the runtime implements), for generated code.

Values

A lungo_value * is a Lean value as a tree of plain data. The API returns owned values, freed with lungo_value_free; constructors of composite values take ownership of their parts; accessors return pointers borrowed from their argument. A value owns the handles and host functions in it: freeing it releases them, lungo_value_clone duplicates them.

Kind Construct Read
LUNGO_NAT lungo_value_nat(uint64_t), lungo_value_nat_parse(const char *digits) lungo_value_get_nat(v, &u64) (false if it does not fit), lungo_value_number_string(v)
LUNGO_INT lungo_value_int(int64_t), lungo_value_int_parse(const char *digits) lungo_value_get_int(v, &i64), lungo_value_number_string(v)
LUNGO_BOOL, LUNGO_UINT8 … LUNGO_ISIZE, LUNGO_FLOAT, LUNGO_FLOAT32 lungo_value_bool(b), lungo_value_uint8(x), … lungo_value_get_bool(v), lungo_value_get_uint8(v), …
LUNGO_CHAR lungo_value_char(uint32_t) (NULL if not a Unicode scalar value) lungo_value_get_char(v)
LUNGO_STRING lungo_value_string(bytes, len), lungo_value_cstring(s) (NULL if not UTF-8) lungo_value_get_string(v, &len) (not NUL-terminated), lungo_value_string_dup(v)
LUNGO_UNIT lungo_value_unit()  
LUNGO_BYTE_ARRAY, LUNGO_FLOAT_ARRAY lungo_value_byte_array(data, len), lungo_value_float_array(data, len) lungo_value_get_bytes(v, &len), lungo_value_get_floats(v, &len)
LUNGO_OPTION lungo_value_none(), lungo_value_some(x) lungo_value_get_option(v) (NULL for none)
LUNGO_LIST, LUNGO_ARRAY lungo_value_list(items, n), lungo_value_array(items, n) lungo_value_count(v), lungo_value_item(v, i)
LUNGO_PROD lungo_value_prod(a, b) lungo_value_first(v), lungo_value_second(v)
LUNGO_EXCEPT lungo_value_ok(x), lungo_value_error(e) lungo_value_is_ok(v), lungo_value_get_except(v)
LUNGO_FUNCTION lungo_value_function(fn_type, f, ctx, drop) lungo_value_call(f, fn_type, args, n, &result, &error)
LUNGO_INDUCTIVE lungo_value_ctor(index, fields, n) (generated constructors call it) lungo_value_ctor_index(v), lungo_value_field_count(v), lungo_value_field(v, i)
LUNGO_OPAQUE (received from Lean)  

lungo_value_kind(v) is the value’s kind; lungo_value_equal(a, b) compares values (opaque values and functions are equal when they are the same handle or host function); strings the API returns are freed with lungo_string_free.

Misusing the API (a null pointer, reading a value of another kind, an index out of range) terminates the process with a message naming the function. Invalid data (a string that is not UTF-8, digits that are not a number) is reported by a NULL result.

Types

A lungo_type * is a type expression: lungo_type_simple(LUNGO_NAT) (and the other kinds without parts), lungo_type_option(t), lungo_type_list(t), lungo_type_array(t), lungo_type_prod(a, b), lungo_type_except(e, a), lungo_type_function(params, n, result), and the named types of a program from its generated descriptors. Constructors borrow their arguments; types are freed with lungo_type_free. Polymorphic functions take the types of their type arguments; function values need their type to be called.

Calls and errors

A generated function returns a status:

Status Meaning
LUNGO_OK *result holds the result (owned).
LUNGO_FAILED The function failed: *error holds an IO.Error (LUNGO_ERROR_IO) or an EIO error value (LUNGO_ERROR_VALUE, lungo_error_value(e)).
LUNGO_MALFORMED The arguments do not match the function’s parameters (LUNGO_ERROR_MALFORMED).

lungo_error_kind(e), lungo_error_message(e) (for display; the value itself for a string error value) and lungo_error_value(e) read an error; lungo_error_free(e) frees it.

Host functions

A host function has the type lungo_function:

int32_t f(void *ctx, const lungo_value *const *args, size_t n, lungo_value **result, lungo_error **error);

It returns LUNGO_OK with its result (owned by the runtime afterwards), or LUNGO_FAILED with an error: lungo_error_io(message) for an IO extern (Lean’s IO.userError; a copy of an error received from Lean, lungo_error_clone(e), raises that error), or lungo_error_from_value(v) for an EIO extern. An error of a host function whose Lean type is neither is fatal to the program. Host functions may be called on any thread. The drop function given with one frees its context once no value or Lean closure refers to it.

The first host function created makes the C API the process’s host; a process has one host, so the C API’s host functions cannot be combined with another language’s support library.

A generated C package implements each operation of the program’s facilities with a host function given to <id>_implement_<facility>_<operation>(f, ctx, drop), before the program initializes. The program’s first use checks that every operation has one, and otherwise ends the process (lungo_panic_facility_missing), naming the facility, the operation and its Lean declaration.

Async programs

A generated function for an export returning Lungo.Async.Program Op α takes a lungo_step * in place of the result, and stores the program’s first step in it:

typedef struct lungo_step {
    uint8_t kind;           /* LUNGO_STEP_DONE or LUNGO_STEP_CALL */
    lungo_value *value;     /* owned: the program's value, or the operation it asks */
    uint64_t resumption;    /* LUNGO_STEP_CALL: what answers the operation */
} lungo_step;

A LUNGO_STEP_DONE step holds the program’s value. A LUNGO_STEP_CALL step holds the operation the host is asked to perform, a value of Op read with its generated accessors, and a resumption. The host performs the operation, in its own time and on any thread, and answers it:

Function Meaning
lungo_resume(resumption, answer, &step, &error) Answers the operation with answer (borrowed), a value of the type the operation is answered with, and stores the program’s next step in step (an output: it is overwritten whatever the status). Returns LUNGO_OK; LUNGO_MALFORMED when the answer is not of that type, which consumes the resumption all the same; or LUNGO_STALE when the resumption was resumed or cancelled already.
lungo_async_cancel(resumption) Gives the program up and releases it: LUNGO_OK, or LUNGO_STALE.
lungo_async_outstanding() The number of programs waiting for an answer.
lungo_step_clear(&step) Frees the step’s value.
lungo_invoke_async(types, entry, sig, sig_len, type_args, n_types, args, n_args, &step, &error) Calls an async entry point, as lungo_invoke calls a function (generated code).

A resumption is used exactly once: resuming or cancelling consumes it, so the program’s continuation never runs twice, and a resumption used again is reported as LUNGO_STALE, never undefined. A program the host neither resumes nor cancels stays in memory until the process ends; a process may end with programs waiting.

Assurance

const char *<id>_assurance_json(void) returns the program’s assurance document, the text of the package’s assurance.json, NUL-terminated and static.

Program API and boundary

These parts are used by generated code and the support libraries; they are documented in lungo.h itself:

  • objects: lungo_obj, boxing, reference counting fast paths (inline) and slow paths, allocation, closures and application;
  • constants and initializers (lungo_lazy_obj, lungo_init_obj, …), the program’s initialization and lungo_run_main;
  • the boundary of the wire format: lungo_types_load, the call context (lungo_call_*, with lungo_call_write_async for an async program’s first step), the resumption of async programs (lungo_async_resume, lungo_async_cancel, lungo_async_outstanding), host calls (lungo_hostcall_*), lungo_closure_call, lungo_set_host, handles (lungo_handle_clone, lungo_handle_release) and buffers (lungo_buffer_alloc, lungo_buffer_free);
  • lungo_invoke and lungo_host_function_new, through which generated C packages implement their API on the value API;
  • lungo_check_layout(lean_type, provider, expected, actual), with which a package using another package’s type (extern types) refuses, before its first call, a provider generated for another layout of the type.