Section 4.7 of the Lean reference manual (version 3.3) gives an example of a division function defined by well-founded recursion. I used the #print
command to inspect the internal term that the equation compiler generates, and I found that it has two holes in it. The code below shows the original function, and an internal term that corresponds to it that was produced by the print command (using Lean version 3.48.0).
def wfdiv : ℕ → ℕ → ℕ
| x y :=
if h : 0 < y ∧ y ≤ x then
have x - y < x,
from nat.sub_lt (lt_of_lt_of_le h.left h.right) h.left,
wfdiv (x - y) y + 1
else
0
#eval wfdiv 25 6
-- result: 4
-- set_option pp.generalized_field_notation false
-- #print wfdiv
-- #print wfdiv._main
-- #print wfdiv._main._pack
def «printed wfdiv._main._pack» : Π (_x : Σ' (ᾰ : ℕ), ℕ), (λ (_x : Σ' (ᾰ : ℕ), ℕ), ℕ) _x :=
λ (_x : Σ' (ᾰ : ℕ), ℕ),
well_founded.fix has_well_founded.wf
(λ (_x : Σ' (ᾰ : ℕ), ℕ),
psigma.cases_on _x
(λ (fst snd : ℕ),
id_rhs ((Π (_y : Σ' (ᾰ : ℕ), ℕ), has_well_founded.r _y ⟨fst, snd⟩ → ℕ) → ℕ)
(λ (_F : Π (_y : Σ' (ᾰ : ℕ), ℕ), has_well_founded.r _y ⟨fst, snd⟩ → ℕ),
dite (0 < snd ∧ snd ≤ fst)
(λ (h : 0 < snd ∧ snd ≤ fst),
have this : fst - snd < fst, from _,
_F ⟨fst - snd, snd⟩ _ + 1)
(λ (h : ¬(0 < snd ∧ snd ≤ fst)), 0))))
_x
def «printed wfdiv._main» : ℕ → ℕ → ℕ
:= λ (ᾰ ᾰ_1 : ℕ), «printed wfdiv._main._pack» ⟨ᾰ, ᾰ_1⟩
#eval «printed wfdiv._main» 25 6
-- result: 4
Note that the second #eval
shows that «printed wfdiv._main»
is still computationally effective even though it has holes in it.
The first hole is in the line have this : fst - snd < fst, from _,
, and
the Lean emacs mode describes it with
30:57: don't know how to synthesize placeholder
context:
_x _x : Σ' (ᾰ : ℕ), ℕ,
fst snd : ℕ,
_F : Π (_y : Σ' (ᾰ : ℕ), ℕ), has_well_founded.r _y ⟨fst, snd⟩ → ℕ,
h : 0 < snd ∧ snd ≤ fst
⊢ fst - snd < fst
The second hole is in the line _F ⟨fst - snd, snd⟩ _ + 1)
, and the Lean emacs mode describes it with
31:43: don't know how to synthesize placeholder
context:
_x _x : Σ' (ᾰ : ℕ), ℕ,
fst snd : ℕ,
_F : Π (_y : Σ' (ᾰ : ℕ), ℕ), has_well_founded.r _y ⟨fst, snd⟩ → ℕ,
h : 0 < snd ∧ snd ≤ fst,
this : fst - snd < fst
⊢ has_well_founded.r ⟨fst - snd, snd⟩ ⟨fst, snd⟩
The <
and has_well_founded.r
terms both have type Prop
when applied to two arguments. Is it correct to say that the equation compiler is omitting these essentially due to "proof irrelevance" (described in section 3.7 of the manual)? In other words, the equation compiler knows that these propositions are true, and also knows that they aren't needed for the actual computation, and so therefore in the code it just leaves them as hypothesized. Is that roughly correct?
Assuming that I'm understanding the situation correctly, then I have one more question about this: would the performance of the code be negatively impacted if those holes were filled in? In other words, is the compiler leaving those terms as holes simply because they are computationally irrelevant and so there's no reason to bother filling them in? or is it leaving them as holes because doing so actually improves performance?