2
votes

I've seen an example of the inspect function in my last question Using the value of a computed function for a proof in agda , but I'm still having trouble wrapping my head around that.

Here's a simple example:

Given the function crazy,

crazy : ℕ -> ℕ
crazy 0 = 10
crazy 1 = 0
crazy 2 = 0
crazy 3 = 1
crazy 4 = 0
crazy xxx = xxx

I want to create a safe function such that safe : {nn : ℕ} -> (id nn) ≢ 0 -> Fin (id nn). In other words it will return one number mod crazy, if you give it a proof crazy is 0. (I know that the example is a little contrived and I would probably be better off using the suc in the function signature)

My first solution is

safebad : {nn : ℕ} -> (crazy nn) ≢ 0 -> Fin (crazy nn)
safebad {1} hh with hh refl
... | ()
safebad {2} hh with hh refl
... | ()
safebad {4} hh with hh refl
... | ()
safebad {0} hh = # 0
safebad {3} hh = # 0
safebad {suc (suc (suc (suc (suc _))))} _ = # 0

But this is long and messy. So I tried to emulate the example in Using the value of a computed function for a proof in agda but could only get so far

safegood : (nn : ℕ) -> (crazy nn) ≢ 0 -> Fin (crazy nn)
safegood nn nez with crazy nn | inspect crazy nn
... | 0 | [ proof ] = ⊥-elim ???
... | _ | _ = # 0

inspect uses Hidden to hide a record of the function application in the type signature, I think. This can then be retrieved with reveal.

This is what I think I understand:

Reveal_is_ seems to hold the value of the hidden f, and x; and the result of x applied to f. [_] will result in the proof of that equality.

⊥-elim takes a proof of contradiction and returns a contradiction.

What do I put into ??? for this to work?

1

1 Answers

3
votes

You are making it needlessly complicated. inspect is only useful when you need to use the proof that the value before pattern matching is equal to the value after pattern matching. Notice that you have nez in the scope which makes this trivial.

What we really want to do is to reduce the assumption crazy nn ≢ 0 to 0 ≢ 0 which we can easily use to construct a contradiction. How do we get crazy nn to reduce to 0? You have already tried the first option - go through all possible crazy arguments and fish for those that indeed reduce crazy nn to 0. The other option is to simply abstract over the value of crazy nn.

First, the goal type before we use with is Fin (crazy nn) and the type of nez is crazy nn ≢ 0. Now, we abstract over crazy nn:

safegood nn nez with crazy nn
... | w = ?

Notice our goal is now Fin w and nez's type is w ≢ 0, much easier to work with! And finally, we pattern match on w:

safegood nn nez with crazy nn
... | zero  = ?
... | suc w = ?

The first goal is now Fin 0 and we have a 0 ≢ 0 as one of our assumptions. This is obviously a nonsense, combining nez with refl gives us a contradiction that can be used by ⊥-elim:

safegood nn nez with crazy nn
... | zero  = ⊥-elim (nez refl)
... | suc w = ?

No inspect in sight! In fact, using inspect here is like making a round trip: you reduce crazy nn to 0 in the types, get a proof that crazy nn ≡ 0 and now you need to "unreduce" 0 back to crazy nn so that you can use nez proof.


For the sake of completeness: you can avoid pattern matching on crazy nn to keep the type of the proof nez intact by using the deprecated inspect:

open Deprecated-inspect
  renaming (inspect to inspect′)

safegood₂ : (nn : ℕ) → crazy nn ≢ 0 → Fin (crazy nn)
safegood₂ nn nez with inspect′ (crazy nn)
... | zero  with-≡ eq = ⊥-elim (nez eq)
... | suc _ with-≡ eq = ?

Since we abstract over inspect′ (crazy nn), no crazy nn subexpressions will get substituted and nez will keep its original type.


Talking about crazy roundtrips: you could use proof to reconstruct nez's original type; again, this is more of a "might be useful to know" than "use this here":

safegood : (nn : ℕ) → crazy nn ≢ 0 → Fin (crazy nn)
safegood nn nez with crazy nn | inspect crazy nn
... | 0 | [ proof ] = ⊥-elim (subst (λ x → x ≢ 0) (sym proof) nez proof)
... | _ | _         = ?