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.
#eval add 2 7
#reduce add 2 7
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.
#eval eval (fun _ => 7)
(AExp.div (AExp.var "y") (AExp.num 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.
#eval fib 10
Example 2. The factorial of five, through the recursion of mul and add.
#eval factorial 5
Example 3. Two to the tenth, through the recursion of power.
#eval power 2 10
Example 4. #reduce normalizes in the kernel and reaches the same value.
#reduce half 7
Example 5. A function into Bool evaluates to a Boolean value.
#eval evenb 10
Example 6. Evaluation runs polymorphic functions as well.
#eval reverse [1, 2, 3]
Example 7. The environment supplies the value of each variable, and the rest is arithmetic.
#eval eval (fun x => if x = "x" then 3 else 0)
(AExp.add (AExp.var "x") (AExp.num 4))
Example 8. Integer division truncates, so 5 / 2 evaluates to 2.
#eval eval (fun _ => 0)
(AExp.div (AExp.num 5) (AExp.num 2))
Example 9. Division on ℤ follows the Euclidean convention, whose remainder is never negative, so −7 / 2 evaluates to −4 rather than −3.
#eval eval (fun _ => 0)
(AExp.div (AExp.num (-7)) (AExp.num 2))
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