Formal Software Verification

3.5. Evaluation🔗

The command #eval runs a program through Lean's compiler, and #reduce normalizes a term symbolically in the kernel. Both compute 9 below, and #eval is the one to reach for at scale.

9#eval add 2 7
9
9#reduce add 2 7
9

Evaluation gives the arithmetic expressions of AExp their meaning. An environment maps variable names to integer values, and eval folds an expression down to its value.

def eval (env : String ) : AExp | AExp.num i => i | AExp.var x => env x | AExp.add e₁ e₂ => eval env e₁ + eval env e₂ | AExp.sub e₁ e₂ => eval env e₁ - eval env e₂ | AExp.mul e₁ e₂ => eval env e₁ * eval env e₂ | AExp.div e₁ e₂ => eval env e₁ / eval env e₂

Division by zero does not fail. Lean's integer division is total, with x / 0 = 0, and the evaluation below computes accordingly. The command #eval and our function eval are unrelated, despite the names.

0#eval eval (fun _ => 7) (AExp.div (AExp.var "y") (AExp.num 0))
0

Computation is also a proof method. An equation whose two sides evaluate to the same value holds by rfl, the term that Lecture 2 used for n * n = 9 at the witness 3. This is definitional computation, and it settles any ground equation, one without variables.

example : add 2 7 = 9 := rfl example : eval (fun _ => 7) (AExp.div (AExp.var "y") (AExp.num 0)) = 0 := rfl

3.5.1. Examples🔗

The examples below run the functions of this lecture and inspect the arithmetic that eval inherits from ℤ.

Example 1. The tenth Fibonacci number, computed by the compiler.

55#eval fib 10
55

Example 2. The factorial of five, through the recursion of mul and add.

120#eval factorial 5
120

Example 3. Two to the tenth, through the recursion of power.

1024#eval power 2 10
1024

Example 4. #reduce normalizes in the kernel and reaches the same value.

3#reduce half 7
3

Example 5. A function into Bool evaluates to a Boolean value.

true#eval evenb 10
true

Example 6. Evaluation runs polymorphic functions as well.

[3, 2, 1]#eval reverse [1, 2, 3]
[3, 2, 1]

Example 7. The environment supplies the value of each variable, and the rest is arithmetic.

7#eval eval (fun x => if x = "x" then 3 else 0) (AExp.add (AExp.var "x") (AExp.num 4))
7

Example 8. Integer division truncates, so 5 / 2 evaluates to 2.

2#eval eval (fun _ => 0) (AExp.div (AExp.num 5) (AExp.num 2))
2

Example 9. Division on ℤ follows the Euclidean convention, whose remainder is never negative, so −7 / 2 evaluates to −4 rather than −3.

-4#eval eval (fun _ => 0) (AExp.div (AExp.num (-7)) (AExp.num 2))
-4

Example 10. Every evaluation above also serves as a proof, since rfl closes an equation whose sides compute to the same value.

example : sumTo 10 = 55 := rfl example : twoPow 8 = 256 := rfl