In questo tutorial scriveremo alcune definizioni Lean sui rettangoli, dimostreremo un fatto che le riguarda, ne genereremo una libreria C con lungo e la useremo. Il progetto Lean è lo stesso dei tutorial per Go, Python, Swift e TypeScript.
Prima di iniziare
Servono:
- CMake 3.20 o successivo e un compilatore C11
- elan, il gestore delle toolchain di Lean
- il comando
lungo
elan toolchain install leanprover/lean4:v4.34.1
brew install machinefabric/tap/lungo # apt, dnf, Windows, cargo: vedi il riferimento del comando lungo
Scrivi il progetto Lean
Crea una directory con un progetto Lake in lean/:
mkdir shapes && cd shapes
mkdir lean
echo 'leanprover/lean4:v4.34.1' > lean/lean-toolchain
lean/lakefile.toml dichiara una libreria il cui modulo radice Lake compila per impostazione
predefinita:
name = "geometry"
version = "0.1.0"
defaultTargets = ["Geometry"]
[[lean_lib]]
name = "Geometry"
lean/Geometry.lean contiene le definizioni e la dimostrazione:
namespace Geometry
structure Rect where
width : Nat
height : Nat
deriving Repr
def area (r : Rect) : Nat := r.width * r.height
def scale (k : Nat) (r : Rect) : Rect :=
{ width := k * r.width, height := k * r.height }
/-- Scaling by `k` multiplies the area by `k * k`. -/
theorem area_scale (k : Nat) (r : Rect) : area (scale k r) = k * k * area r := by
simp only [area, scale]
exact Nat.mul_mul_mul_comm k r.width k r.height
/-- The rectangle with the largest area, if any. -/
def largest (rs : List Rect) : Option Rect :=
rs.foldl (fun best r => match best with
| some b => if area r > area b then some r else some b
| none => some r) none
end Geometry
Crea una volta il manifest di Lake (fa parte del progetto):
(cd lean && lake update)
Genera la libreria
lungo.toml indica il progetto Lake e dove va la libreria C:
project = "lean"
[c]
out = "geometry-c"
lungo generate
La prima esecuzione compila il progetto Lean, e quindi controlla la dimostrazione: cambia
k * k in k nel teorema e lungo generate fallisce con l’errore di Lean. geometry-c è un
progetto CMake della libreria geometry, la cui API è include/geometry.h; aggiungi
.lungo/ (lo spazio di lavoro di lungo) a .gitignore.
Chiamala da C
Il CMakeLists.txt del programma:
cmake_minimum_required(VERSION 3.20)
project(shapes C)
add_subdirectory(geometry-c)
add_executable(shapes main.c)
target_link_libraries(shapes PRIVATE geometry)
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "geometry.h"
/* Exits with the error of a failed call. */
static lungo_value *check(int32_t status, lungo_value *result, lungo_error *error) {
if (status != LUNGO_OK) {
fprintf(stderr, "error: %s\n", lungo_error_message(error));
exit(1);
}
return result;
}
static void print_rect(const char *label, const lungo_value *r) {
char *w = lungo_value_number_string(geometry_rect_width(r));
char *h = lungo_value_number_string(geometry_rect_height(r));
printf("%s: %s x %s\n", label, w, h);
lungo_string_free(w);
lungo_string_free(h);
}
int main(void) {
lungo_value *result = NULL;
lungo_error *error = NULL;
lungo_value *r = geometry_rect_mk(lungo_value_nat(3), lungo_value_nat(4));
lungo_value *area = check(geometry_area(r, &result, &error), result, error);
char *digits = lungo_value_number_string(area);
printf("area: %s\n", digits);
lungo_string_free(digits);
lungo_value *ten = lungo_value_nat(10);
lungo_value *bigger = check(geometry_scale(ten, r, &result, &error), result, error);
print_rect("scaled", bigger);
lungo_value *items[2] = {lungo_value_clone(r), lungo_value_clone(bigger)};
lungo_value *list = lungo_value_list(items, 2);
lungo_value *best = check(geometry_largest(list, &result, &error), result, error);
if (lungo_value_get_option(best)) print_rect("largest", lungo_value_get_option(best));
lungo_value_free(best);
lungo_value_free(list);
lungo_value_free(bigger);
lungo_value_free(ten);
lungo_value_free(area);
lungo_value_free(r);
return 0;
}
$ cmake -S . -B build && cmake --build build && ./build/shapes
area: 12
scaled: 30 x 40
largest: 30 x 40
CMake scarica il runtime di lungo per la piattaforma e ne controlla il digest SHA-256 prima di
collegarlo. Ogni valore Lean è un lungo_value: i costruttori (geometry_rect_mk) prendono
possesso delle loro parti, gli accessori (geometry_rect_width) le prendono in prestito, e
ogni valore restituito dall’API va liberato una volta.
Che cosa abbiamo fatto
Abbiamo generato una libreria C da un progetto Lake di cui Lean ha controllato le dimostrazioni; esegue il codice compilato di Lean sul runtime di lungo. Prossimi passi: