Verified Functions
fx.types.verified.verify checks a HOAS implementation against a type,
evaluates it, and extracts a Nix value. The result can be called like an
ordinary function, but it first passed through the kernel.
First functions
The smallest verified programs are ordinary unary functions. The type says what the extracted Nix function accepts and returns; the implementation is written with verified HOAS constructors.
succFn = v.verify (H.forall "x" H.nat (_: H.nat))
(v.fn "x" H.nat (x: H.succ x));
succApply5 = succFn 5 == 6;
notFn = v.verify (H.forall "b" H.bool (_: H.bool))
(v.fn "b" H.bool (b:
v.if_ H.bool b { then_ = v.false_; else_ = v.true_; }));
Recursion over naturals
v.match builds the eliminator for naturals. Addition uses the
first argument as the scrutinee and returns a function awaiting the
second argument.
addFn = v.verify (H.forall "m" H.nat (_: H.forall "n" H.nat (_: H.nat)))
(v.fn "m" H.nat (m: v.fn "n" H.nat (n:
v.match H.nat m {
zero = n;
succ = _k: ih: H.succ ih;
})));
add2and3 = addFn 2 3 == 5;
Lists and pipelines
List combinators remain checked HOAS terms until extraction. The composed example filters zeros out of a list and folds the remaining values into a sum.
mapSuccResult = v.verify (H.listOf H.nat)
(v.map H.nat H.nat
(v.fn "x" H.nat (x: H.succ x))
(H.cons (v.nat 0) (H.cons (v.nat 1)
(H.cons (v.nat 2) H.nil))));
composedResult =
let
input = H.cons (v.nat 0) (H.cons (v.nat 3)
(H.cons (v.nat 0) (H.cons (v.nat 2)
(H.cons (v.nat 1) H.nil))));
nonZero = v.fn "n" H.nat (n:
v.match H.bool n {
zero = v.false_;
succ = _k: _ih: v.true_;
});
in
v.verify H.nat (v.fold H.nat H.nat (v.nat 0) addCombine
(v.filter H.nat nonZero input));
Sums, lets, pairs, and records
Verified extraction also covers the richer HOAS constructors used by nix-effects types: coproduct elimination, local bindings, sigma pairs, and record field projection.
sumLeftResult = v.verify H.nat
(v.matchSum H.nat H.bool H.nat (v.inl H.nat H.bool (v.nat 5)) {
left = x: H.succ x;
right = b: v.if_ H.nat b { then_ = v.nat 1; else_ = v.nat 0; };
});
recordGetX = v.verify
(H.forall "r"
(H.record [{ name = "x"; type = H.nat; } { name = "y"; type = H.bool; }])
(_: H.nat))
(v.fn "r"
(H.record [{ name = "x"; type = H.nat; } { name = "y"; type = H.bool; }])
(r: v.field
(H.record [{ name = "x"; type = H.nat; } { name = "y"; type = H.bool; }])
"x"
r));
Strings and records
The final examples use kernel-verified string predicates directly and then combine them with record projection.
strEqFn = v.verify (H.forall "a" H.string (_: H.forall "b" H.string (_: H.bool)))
(v.fn "a" H.string (a: v.fn "b" H.string (b: v.strEq a b)));
recordStrEqFn =
let
recTy = H.record [
{ name = "name"; type = H.string; }
{ name = "target"; type = H.string; }
];
in
v.verify (H.forall "r" recTy (_: H.bool))
(v.fn "r" recTy (r:
v.strEq (v.field recTy "name" r) (v.field recTy "target" r)));